@nyris/nyris-webapp 0.3.9 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/asset-manifest.json +11 -11
- package/build/index.html +1 -1
- package/build/js/settings.example.js +30 -0
- package/build/{precache-manifest.a97813497ab8d37548141e5e2618d0dc.js → precache-manifest.fa9930c6ba4e9fb0ebad3869127ef308.js} +9 -9
- package/build/service-worker.js +1 -1
- package/build/static/css/main.6633770c.chunk.css +2 -0
- package/build/static/css/main.6633770c.chunk.css.map +1 -0
- package/build/static/js/2.0798bb8b.chunk.js +3 -0
- package/build/static/js/{2.6e13adbe.chunk.js.LICENSE.txt → 2.0798bb8b.chunk.js.LICENSE.txt} +0 -0
- package/build/static/js/2.0798bb8b.chunk.js.map +1 -0
- package/build/static/js/main.31212715.chunk.js +2 -0
- package/build/static/js/main.31212715.chunk.js.map +1 -0
- package/package.json +2 -2
- package/public/js/settings.example.js +30 -0
- package/src/App.tsx +221 -203
- package/src/actions/nyrisAppActions.ts +69 -65
- package/src/actions/searchActions.ts +288 -196
- package/src/components/FiltersList.tsx +106 -57
- package/src/components/Header.tsx +29 -17
- package/src/components/SelectedFiltersSummary.tsx +84 -0
- package/src/components/Sidebar.tsx +41 -34
- package/src/epics/index.ts +128 -108
- package/src/epics/search.ts +114 -82
- package/src/index.css +95 -6
- package/src/index.tsx +89 -95
- package/src/utils.ts +5 -0
- package/build/static/css/main.0481043c.chunk.css +0 -2
- package/build/static/css/main.0481043c.chunk.css.map +0 -1
- package/build/static/js/2.6e13adbe.chunk.js +0 -3
- package/build/static/js/2.6e13adbe.chunk.js.map +0 -1
- package/build/static/js/main.f5da7aa4.chunk.js +0 -2
- package/build/static/js/main.f5da7aa4.chunk.js.map +0 -1
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
const Header = () =>{
|
|
4
|
-
return (
|
|
3
|
+
const Header = () => {
|
|
4
|
+
return (
|
|
5
5
|
<div id="header">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
<section id="branding"></section>
|
|
7
|
+
<div id="menu" className="menuWrap" role="navigation">
|
|
8
|
+
<ul>
|
|
9
|
+
<li>
|
|
10
|
+
<a
|
|
11
|
+
href="https://nyris.io/imprint/#privacy"
|
|
12
|
+
target="_blank"
|
|
13
|
+
rel="noopener noreferrer"
|
|
14
|
+
>
|
|
15
|
+
Privacy Policy
|
|
16
|
+
</a>
|
|
17
|
+
</li>
|
|
18
|
+
<li>
|
|
19
|
+
<a
|
|
20
|
+
href="https://nyris.io/"
|
|
21
|
+
target="_blank"
|
|
22
|
+
rel="noopener noreferrer"
|
|
23
|
+
>
|
|
24
|
+
Visit our Website
|
|
25
|
+
</a>
|
|
26
|
+
</li>
|
|
27
|
+
</ul>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
export default Header;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BsX } from "react-icons/bs";
|
|
3
|
+
import { connect, ConnectedProps, useDispatch } from "react-redux";
|
|
4
|
+
import { AppState } from "../types";
|
|
5
|
+
import { capitalizeFirstLetter } from "../utils";
|
|
6
|
+
import {
|
|
7
|
+
removeFromSelectedFilters,
|
|
8
|
+
clearAllSelectedFilters,
|
|
9
|
+
filterChanged,
|
|
10
|
+
} from "../actions/searchActions";
|
|
11
|
+
|
|
12
|
+
const SelectedFiltersSummary: React.FC<selectedFiltersSummaryProps> = ({
|
|
13
|
+
search,
|
|
14
|
+
}) => {
|
|
15
|
+
const dispatch = useDispatch();
|
|
16
|
+
const handleRemoveFilterButtonClick = (key: string, value: string) => {
|
|
17
|
+
dispatch(removeFromSelectedFilters(key, value));
|
|
18
|
+
dispatch(filterChanged());
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const clearAllSelectedFiltersFromList = () => {
|
|
22
|
+
dispatch(clearAllSelectedFilters());
|
|
23
|
+
dispatch(filterChanged());
|
|
24
|
+
};
|
|
25
|
+
const selectedValues = ([] as string[]).concat.apply(
|
|
26
|
+
[],
|
|
27
|
+
Array.from(search.selectedFilters.values())
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="wrap-box-refinements">
|
|
32
|
+
<ul>
|
|
33
|
+
{Array.from(search.selectedFilters.keys()).map(
|
|
34
|
+
(filterKey) =>
|
|
35
|
+
search.selectedFilters.get(filterKey) &&
|
|
36
|
+
search.selectedFilters.get(filterKey)!.map((val) => {
|
|
37
|
+
return (
|
|
38
|
+
<li>
|
|
39
|
+
<button
|
|
40
|
+
onClick={() =>
|
|
41
|
+
handleRemoveFilterButtonClick(filterKey, val)
|
|
42
|
+
}
|
|
43
|
+
>
|
|
44
|
+
<span className="summary-label">
|
|
45
|
+
<div className="summary-label-key-text">
|
|
46
|
+
{capitalizeFirstLetter(val)}
|
|
47
|
+
<BsX />
|
|
48
|
+
</div>
|
|
49
|
+
</span>
|
|
50
|
+
</button>
|
|
51
|
+
</li>
|
|
52
|
+
);
|
|
53
|
+
})
|
|
54
|
+
)}
|
|
55
|
+
<li>
|
|
56
|
+
<button
|
|
57
|
+
className={
|
|
58
|
+
search.selectedFilters &&
|
|
59
|
+
search.selectedFilters.size > 0 &&
|
|
60
|
+
selectedValues &&
|
|
61
|
+
selectedValues.length > 0
|
|
62
|
+
? "clear-all-filters "
|
|
63
|
+
: "clear-all-filters-hidden"
|
|
64
|
+
}
|
|
65
|
+
onClick={() => clearAllSelectedFiltersFromList()}
|
|
66
|
+
>
|
|
67
|
+
Clear All
|
|
68
|
+
</button>
|
|
69
|
+
</li>
|
|
70
|
+
</ul>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const mapStateToProps = (state: AppState) => ({
|
|
76
|
+
showPart: state.nyrisDesign.showPart,
|
|
77
|
+
search: {
|
|
78
|
+
filters: state.search.filters,
|
|
79
|
+
selectedFilters: state.search.selectedFilters,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
const connector = connect(mapStateToProps);
|
|
83
|
+
type selectedFiltersSummaryProps = ConnectedProps<typeof connector>;
|
|
84
|
+
export default connect(mapStateToProps)(SelectedFiltersSummary);
|
|
@@ -1,42 +1,49 @@
|
|
|
1
|
-
import React, { useState } from
|
|
2
|
-
import {RiMenuLine} from
|
|
3
|
-
import { Filter } from
|
|
4
|
-
import
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { RiMenuLine } from "react-icons/ri";
|
|
3
|
+
import { Filter } from "../../../nyris-api/index";
|
|
4
|
+
import FiltersList from "./FiltersList";
|
|
5
5
|
|
|
6
6
|
interface SidebarProps {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
filters: Filter[];
|
|
8
|
+
selectedFilters: Map<string, string[]>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const Sidebar: React.FC<SidebarProps> = ({filters, selectedFilters}) =>{
|
|
11
|
+
const Sidebar: React.FC<SidebarProps> = ({ filters, selectedFilters }) => {
|
|
12
|
+
console.log(filters);
|
|
12
13
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
13
|
-
const handleToggler =()=>{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
14
|
+
const handleToggler = () => {
|
|
15
|
+
if (isExpanded) {
|
|
16
|
+
setIsExpanded(false);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
setIsExpanded(true);
|
|
20
|
+
};
|
|
20
21
|
return (
|
|
21
|
-
|
|
22
|
-
<div
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
<div className={isExpanded ? "sidebar" : "Sidebar collapsed"}>
|
|
23
|
+
<div
|
|
24
|
+
className={
|
|
25
|
+
isExpanded ? "sidebarContent" : "sidebarContent overflowHidden"
|
|
26
|
+
}
|
|
27
|
+
>
|
|
28
|
+
<div className="sidebarHeader">
|
|
29
|
+
<RiMenuLine className="sidebar-icon" onClick={handleToggler} />
|
|
30
|
+
</div>
|
|
31
|
+
<div
|
|
32
|
+
className={
|
|
33
|
+
isExpanded ? "Sidebar-items" : "Sidebar-items collapsedHide"
|
|
34
|
+
}
|
|
35
|
+
>
|
|
36
|
+
{filters &&
|
|
37
|
+
filters.map((x) => {
|
|
38
|
+
let selectedValues = x.key
|
|
39
|
+
? selectedFilters.get(x.key)
|
|
40
|
+
: undefined;
|
|
41
|
+
return <FiltersList filter={x} selectedValues={selectedValues} />;
|
|
42
|
+
})}
|
|
43
|
+
</div>
|
|
36
44
|
</div>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
41
48
|
|
|
42
|
-
export default Sidebar
|
|
49
|
+
export default Sidebar;
|
package/src/epics/index.ts
CHANGED
|
@@ -9,146 +9,166 @@ import {AppAction} from "../types";
|
|
|
9
9
|
import {selectFirstCenteredRegion} from "@nyris/nyris-api";
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
const historyEpic: EpicConf = (action$, state$, {history}) => action$.pipe(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
history.goBack();
|
|
27
|
-
}
|
|
28
|
-
}),
|
|
29
|
-
ignoreElements()
|
|
13
|
+
ofType('SHOW_RESULTS', 'SHOW_START'),
|
|
14
|
+
tap((action) => {
|
|
15
|
+
let { type } = action;
|
|
16
|
+
if (type === 'SHOW_RESULTS' && history.location.pathname !== '/results') {
|
|
17
|
+
history.push('/results');
|
|
18
|
+
}
|
|
19
|
+
if (type === 'SHOW_START' && history.location.pathname !== '/') {
|
|
20
|
+
history.goBack();
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
ignoreElements()
|
|
30
24
|
);
|
|
31
25
|
|
|
32
26
|
const onSearchSuccessShowResults: EpicConf = (action$) => action$.pipe(
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
ofType('SEARCH_REQUEST_SUCCEED'),
|
|
28
|
+
map(showResults)
|
|
35
29
|
);
|
|
36
30
|
|
|
37
31
|
const onSearchSuccessRedirectToSite: EpicConf = (action$, state$) => action$.pipe(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
32
|
+
ofType('SEARCH_REQUEST_SUCCEED'),
|
|
33
|
+
withLatestFrom(state$),
|
|
34
|
+
tap(([action, {settings}]) => {
|
|
35
|
+
if (action.type !== 'SEARCH_REQUEST_SUCCEED' || !action.results || action.results.length !== 1) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const firstLink = action.results[0].l;
|
|
40
|
+
const instantRedirectPatterns = settings.instantRedirectPatterns;
|
|
41
|
+
if (!instantRedirectPatterns.find(r => new RegExp(r).test(firstLink))) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
window.location.href = firstLink;
|
|
45
|
+
}),
|
|
46
|
+
ignoreElements()
|
|
53
47
|
);
|
|
54
48
|
|
|
55
49
|
const onSearchSuccessShowFeedbackDelayed: EpicConf = (action$) => action$.pipe(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
ofType('SEARCH_REQUEST_SUCCEED'),
|
|
51
|
+
delay(3000),
|
|
52
|
+
map(showFeedback)
|
|
59
53
|
);
|
|
60
54
|
|
|
61
55
|
|
|
62
56
|
const startSearchOnImageLoaded: EpicConf = (action$, state$) => action$.pipe(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
ofType('IMAGE_LOADED'),
|
|
58
|
+
withLatestFrom(state$),
|
|
59
|
+
switchMap(async ([action, {settings}]) : Promise<AppAction> => {
|
|
60
|
+
if (action.type !== 'IMAGE_LOADED') {
|
|
61
|
+
throw new Error(`Wrong action type ${action.type}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let { image } = action;
|
|
65
|
+
|
|
66
|
+
if (settings.regions) {
|
|
67
|
+
return searchRegions(image.canvas);
|
|
68
|
+
}
|
|
69
|
+
return searchOffersForImage(image.canvas);
|
|
70
|
+
})
|
|
77
71
|
);
|
|
78
72
|
|
|
79
73
|
const startSearchOnCadLoaded: EpicConf = (action$, state$) => action$.pipe(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
74
|
+
ofType('CAD_LOADED'),
|
|
75
|
+
withLatestFrom(state$),
|
|
76
|
+
switchMap(async ([action, {settings}]) : Promise<AppAction> => {
|
|
77
|
+
if (action.type !== 'CAD_LOADED') {
|
|
78
|
+
throw new Error(`Wrong action type ${action.type}`);
|
|
79
|
+
}
|
|
80
|
+
let { file } = action;
|
|
81
|
+
return searchOffersForCad(file);
|
|
82
|
+
})
|
|
89
83
|
);
|
|
90
84
|
|
|
91
85
|
const startSearchOnRegionsSuccessful: EpicConf = (action$, state$) => action$.pipe(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
86
|
+
ofType('REGION_REQUEST_SUCCEED'),
|
|
87
|
+
withLatestFrom(state$),
|
|
88
|
+
switchMap(async ([action, { search: { requestImage}}]) : Promise<AppAction> => {
|
|
89
|
+
if (action.type !== 'REGION_REQUEST_SUCCEED') {
|
|
90
|
+
throw new Error(`Wrong action type ${action.type}`);
|
|
91
|
+
}
|
|
92
|
+
if (!requestImage) {
|
|
93
|
+
throw new Error(`No requestImage`);
|
|
94
|
+
}
|
|
95
|
+
let { regions } = action;
|
|
96
|
+
|
|
97
|
+
let selection = selectFirstCenteredRegion(regions, 0.3, {x1: 0, x2: 1, y1: 0, y2: 1});
|
|
98
|
+
return searchOffersForImage(requestImage.canvas, selection);
|
|
99
|
+
})
|
|
106
100
|
);
|
|
107
101
|
|
|
108
102
|
const startSearchOnRegionsFailed: EpicConf = (action$, state$) => action$.pipe(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
103
|
+
ofType('REGION_REQUEST_FAIL'),
|
|
104
|
+
withLatestFrom(state$),
|
|
105
|
+
switchMap(async ([action, { search: { requestImage}}]) : Promise<AppAction> => {
|
|
106
|
+
if (action.type !== 'REGION_REQUEST_FAIL') {
|
|
107
|
+
throw new Error(`Wrong action type ${action.type}`);
|
|
108
|
+
}
|
|
109
|
+
if (!requestImage) {
|
|
110
|
+
throw new Error(`No requestImage`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return searchOffersForImage(requestImage.canvas);
|
|
114
|
+
})
|
|
121
115
|
);
|
|
122
116
|
|
|
123
117
|
const startSearchOnRegionChange: EpicConf = (action$, state$) => action$.pipe(
|
|
124
|
-
|
|
125
|
-
|
|
118
|
+
ofType('REGION_CHANGED'),
|
|
119
|
+
debounceTime(1200),
|
|
120
|
+
withLatestFrom(state$),
|
|
121
|
+
switchMap(async ([action, { search: { requestImage}}]) : Promise<AppAction> => {
|
|
122
|
+
if (action.type !== 'REGION_CHANGED') {
|
|
123
|
+
throw new Error(`Wrong action type ${action.type}`);
|
|
124
|
+
}
|
|
125
|
+
if (!requestImage) {
|
|
126
|
+
throw new Error(`No requestImage`);
|
|
127
|
+
}
|
|
128
|
+
let { normalizedRect } = action;
|
|
129
|
+
return searchOffersForImage(requestImage.canvas, normalizedRect);
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
const startSearchOnFilterChange: EpicConf = (action$, state$) =>
|
|
135
|
+
action$.pipe(
|
|
136
|
+
ofType("FILTER_CHANGED"),
|
|
126
137
|
withLatestFrom(state$),
|
|
127
|
-
switchMap(
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
switchMap(
|
|
139
|
+
async ([
|
|
140
|
+
action,
|
|
141
|
+
{
|
|
142
|
+
search: { requestImage },
|
|
143
|
+
},
|
|
144
|
+
]): Promise<AppAction> => {
|
|
145
|
+
if (action.type !== "FILTER_CHANGED") {
|
|
146
|
+
throw new Error(`Wrong action type ${action.type}`);
|
|
130
147
|
}
|
|
131
|
-
if (
|
|
132
|
-
|
|
148
|
+
if (requestImage) {
|
|
149
|
+
return searchOffersForImage(requestImage.canvas);
|
|
150
|
+
} else {
|
|
151
|
+
return { type: "FILTER_CHANGED_FAIL" };
|
|
133
152
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
);
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
);
|
|
138
156
|
|
|
157
|
+
//const startSearchOnClearingAllFilters:
|
|
139
158
|
|
|
140
159
|
const rootEpic = combineEpics(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
160
|
+
searchEpics,
|
|
161
|
+
feedbackEpics,
|
|
162
|
+
historyEpic,
|
|
163
|
+
startSearchOnImageLoaded,
|
|
164
|
+
startSearchOnCadLoaded,
|
|
165
|
+
startSearchOnRegionsSuccessful,
|
|
166
|
+
startSearchOnRegionsFailed,
|
|
167
|
+
startSearchOnRegionChange,
|
|
168
|
+
onSearchSuccessShowResults,
|
|
169
|
+
onSearchSuccessShowFeedbackDelayed,
|
|
170
|
+
onSearchSuccessRedirectToSite,
|
|
171
|
+
startSearchOnFilterChange
|
|
152
172
|
);
|
|
153
173
|
|
|
154
174
|
export default rootEpic;
|