@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
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var settings = {
|
|
2
|
+
"apiKey": "xxx",
|
|
3
|
+
"maxWidth": 500,
|
|
4
|
+
"maxHeight": 500,
|
|
5
|
+
"jpegQuality": 0.9,
|
|
6
|
+
"regions": true,
|
|
7
|
+
"preview": true,
|
|
8
|
+
"baseUrl": "https://api.nyris.io",
|
|
9
|
+
"xOptions": "",
|
|
10
|
+
"exampleImages": [
|
|
11
|
+
"https://img.nyris.io/demo/everybag/kissen.jpg",
|
|
12
|
+
"https://img.nyris.io/demo/everybag/aspirin.jpg",
|
|
13
|
+
"https://img.nyris.io/demo/everybag/lego.jpg",
|
|
14
|
+
"https://img.nyris.io/demo/everybag/wdr_add_2.jpg",
|
|
15
|
+
"https://img.nyris.io/demo/everybag/mb-dle-4.jpg",
|
|
16
|
+
"https://img.nyris.io/demo/everybag/1.jpg",
|
|
17
|
+
"https://img.nyris.io/demo/everybag/5.jpg",
|
|
18
|
+
"https://img.nyris.io/demo/everybag/6.jpg"
|
|
19
|
+
],
|
|
20
|
+
"instantRedirectPatterns": [
|
|
21
|
+
'^https?://(www.)?youtube.com/',
|
|
22
|
+
'^https?://(www.)?youtu.be/',
|
|
23
|
+
'^https?://(www.)?vimeo.com/',
|
|
24
|
+
'^https?://(www.)?dailymotion.com/',
|
|
25
|
+
'^https?://(www.)?dai.ly/'
|
|
26
|
+
]
|
|
27
|
+
};
|
|
28
|
+
settings["customSearchRequest"] = null;
|
|
29
|
+
settings["responseHook"] = null;
|
|
30
|
+
|
package/src/App.tsx
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import './App.css';
|
|
2
|
+
import React, {useEffect, useState} from 'react';
|
|
3
|
+
import Result from './components/Result';
|
|
4
|
+
import ExampleImages from './components/ExampleImages';
|
|
5
|
+
import Feedback from './components/Feedback';
|
|
6
|
+
import CategoryFilter from "./components/CategoryFilter";
|
|
7
|
+
import PredictedCategories from "./components/PredictedCategories";
|
|
8
|
+
import Codes from "./components/Codes";
|
|
9
|
+
import {Code, CategoryPrediction, RectCoords, Region, cadExtensions, Filter} from "@nyris/nyris-api";
|
|
4
10
|
import { useDropzone} from "react-dropzone";
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
+
import classNames from 'classnames';
|
|
12
|
+
import {Animate, NodeGroup} from "react-move";
|
|
13
|
+
import {AppSettings, MDSettings, CanvasWithId} from "./types";
|
|
14
|
+
import {NyrisAppPart, NyrisFeedbackState} from "./actions/nyrisAppActions";
|
|
15
|
+
import {makeFileHandler, Capture, Preview} from "@nyris/nyris-react-components";
|
|
16
|
+
import {Box,Snackbar} from "@material-ui/core";
|
|
17
|
+
import MuiAlert, { AlertProps } from '@material-ui/lab/Alert';
|
|
18
|
+
import { useDispatch } from "react-redux";
|
|
11
19
|
import { loadFilters } from "./actions/searchActions";
|
|
12
|
-
import Result from "./components/Result";
|
|
13
20
|
import Sidebar from "./components/Sidebar";
|
|
14
21
|
import Header from "./components/Header";
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
import Codes from "./components/Codes";
|
|
18
|
-
import PredictedCategories from "./components/PredictedCategories";
|
|
19
|
-
import ExampleImages from "./components/ExampleImages";
|
|
20
|
-
import {makeFileHandler, Capture, Preview} from "@nyris/nyris-react-components";
|
|
21
|
-
import { RectCoords, cadExtensions, CategoryPrediction, Code, Region, Filter} from "@nyris/nyris-api";
|
|
22
|
-
import { AppSettings, CanvasWithId, MDSettings } from "./types";
|
|
23
|
-
import { NyrisAppPart, NyrisFeedbackState } from "./actions/nyrisAppActions";
|
|
22
|
+
import SelectedFiltersSummary from "./components/SelectedFiltersSummary";
|
|
23
|
+
|
|
24
24
|
|
|
25
25
|
export interface AppHandlers {
|
|
26
26
|
onExampleImageClick: (url: string) => void,
|
|
@@ -35,210 +35,228 @@ export interface AppHandlers {
|
|
|
35
35
|
onSelectionChange: (r: RectCoords) => void,
|
|
36
36
|
onPositiveFeedback: () => void,
|
|
37
37
|
onNegativeFeedback: () => void,
|
|
38
|
-
onCloseFeedback: () => void
|
|
38
|
+
onCloseFeedback: () => void,
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
|
|
40
42
|
export interface AppProps {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
43
|
+
search: {
|
|
44
|
+
results: any[];
|
|
45
|
+
requestId?: string;
|
|
46
|
+
duration?: number;
|
|
47
|
+
categoryPredictions: CategoryPrediction[];
|
|
48
|
+
codes: Code[];
|
|
49
|
+
filterOptions: string[];
|
|
50
|
+
errorMessage?: string;
|
|
51
|
+
regions: Region[];
|
|
52
|
+
previewSelection: RectCoords;
|
|
53
|
+
toastErrorMessage?: string;
|
|
54
|
+
filters: Filter[];
|
|
55
|
+
selectedFilters: Map<string, string[]>;
|
|
56
|
+
};
|
|
57
|
+
previewImage?: CanvasWithId;
|
|
58
|
+
settings: AppSettings;
|
|
59
|
+
loading: boolean;
|
|
60
|
+
showPart: NyrisAppPart;
|
|
61
|
+
feedbackState: NyrisFeedbackState;
|
|
62
|
+
handlers: AppHandlers;
|
|
63
|
+
mdSettings: MDSettings;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
|
|
67
|
+
function Alert(props: AlertProps) {
|
|
68
|
+
return <MuiAlert elevation={6} variant="filled" {...props} />;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const App: React.FC<AppProps> = ({
|
|
73
|
+
search: {results, regions, previewSelection, requestId, duration, errorMessage, filterOptions, categoryPredictions, codes, toastErrorMessage, filters, selectedFilters},
|
|
74
|
+
showPart, settings, handlers, loading, previewImage, feedbackState
|
|
67
75
|
}) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
|
|
77
|
+
const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop: (fs: File[]) => handlers.onFileDropped(fs[0])});
|
|
78
|
+
|
|
79
|
+
const minPreviewHeight = 400;
|
|
80
|
+
const halfOfTheScreenHeight = Math.floor(window.innerHeight * 0.45);
|
|
81
|
+
const maxPreviewHeight = Math.max(minPreviewHeight, halfOfTheScreenHeight);
|
|
82
|
+
const acceptTypes =
|
|
83
|
+
[ 'image/*' ].concat(
|
|
84
|
+
settings.cadSearch ? cadExtensions : []
|
|
85
|
+
).join(',');
|
|
86
|
+
const [toastOpen, setToastOpen] = useState(false);
|
|
87
|
+
|
|
88
|
+
const dispatch = useDispatch();
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (toastErrorMessage !== "") {
|
|
92
|
+
setToastOpen(true);
|
|
93
|
+
}
|
|
94
|
+
}, [toastErrorMessage]);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
87
97
|
dispatch(loadFilters());
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
}, [showPart, dispatch]);
|
|
99
|
+
|
|
90
100
|
return (
|
|
91
101
|
<React.Fragment>
|
|
92
|
-
<Header/>
|
|
93
|
-
<div className="wrapperBody">
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
onClick: e => {
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
102
|
+
<Header />
|
|
103
|
+
<div className="wrapperBody">
|
|
104
|
+
<Sidebar filters={filters} selectedFilters={selectedFilters} />
|
|
105
|
+
|
|
106
|
+
<div className="mainContent">
|
|
107
|
+
{showPart === 'camera' &&
|
|
108
|
+
<Capture onCaptureComplete={handlers.onCaptureComplete} onCaptureCanceled={handlers.onCaptureCanceled}
|
|
109
|
+
useAppText='Use default camera app'/>}
|
|
110
|
+
<SelectedFiltersSummary />
|
|
111
|
+
<div className={classNames('headSection', {hidden: showPart === 'results'})} id="headSection">
|
|
112
|
+
<div
|
|
113
|
+
{...getRootProps({
|
|
114
|
+
onClick: (e) => {
|
|
115
|
+
e.stopPropagation();
|
|
116
|
+
},
|
|
117
|
+
})} className={classNames('wrapper', 'dragAndDropActionArea', {'fileIsHover': isDragActive})}>
|
|
118
|
+
<div className={classNames("contentWrap")}>
|
|
119
|
+
<Box
|
|
120
|
+
display="flex"
|
|
121
|
+
flexDirection="column"
|
|
122
|
+
justifyContent="center"
|
|
123
|
+
alignItems="center"
|
|
124
|
+
minHeight="100vh"
|
|
125
|
+
>
|
|
126
|
+
<section className="uploadImage">
|
|
127
|
+
<input type="button" name="file" id="capture" className="inputfile" accept="image/*"
|
|
128
|
+
capture="environment" onClick={handlers.onCameraClick}/>
|
|
129
|
+
<input type="file" name="file" id="capture_file" className="inputfile" accept={acceptTypes}
|
|
130
|
+
capture="environment"/>
|
|
131
|
+
<input {...getInputProps()} type="file" name="file" id="select_file" className="inputfile"
|
|
132
|
+
accept={acceptTypes}
|
|
133
|
+
onChange={makeFileHandler(handlers.onSelectFile)}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<div className="onDesktop">
|
|
137
|
+
Drop an image
|
|
138
|
+
<div className="smallText">or</div>
|
|
139
|
+
</div>
|
|
140
|
+
<div className="onMobile camIcon">
|
|
141
|
+
<img src="./images/ic_cam_large.svg" alt="Camera" />
|
|
142
|
+
</div>
|
|
143
|
+
<label
|
|
144
|
+
htmlFor="capture"
|
|
145
|
+
className="btn primary onMobile"
|
|
146
|
+
style={{ marginBottom: "2em", width: "12em" }}
|
|
147
|
+
>
|
|
148
|
+
<span className="onMobile">Take a picture</span>
|
|
149
|
+
</label>
|
|
150
|
+
<br />
|
|
151
|
+
<label htmlFor="select_file" className="btn primary" style={{width: '22em'}}>
|
|
152
|
+
<span>Select a file</span>
|
|
153
|
+
</label>
|
|
154
|
+
<label htmlFor="capture" className="mobileUploadHandler onMobile"/>
|
|
155
|
+
</section>
|
|
156
|
+
<ExampleImages images={settings.exampleImages} onExampleImageClicked={handlers.onExampleImageClick}/>
|
|
139
157
|
</Box>
|
|
140
|
-
|
|
158
|
+
</div>
|
|
141
159
|
</div>
|
|
142
|
-
|
|
143
|
-
<div className="headerSeparatorTop"/>
|
|
144
|
-
<div className="headerSeparatorBack"/>
|
|
160
|
+
|
|
161
|
+
<div className="headerSeparatorTop" />
|
|
162
|
+
<div className="headerSeparatorBack" />
|
|
145
163
|
<div className={classNames('tryDifferent', {hidden: showPart !== 'results'})}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<br style={{clear: 'both'}}/>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
|
|
154
|
-
<section className={classNames({hideResults: showPart !== 'results'},'results', {resultsActive: showPart === 'results'}, (results.length === 1 ? 'singleProduct' : 'multipleProducts'))}>
|
|
155
|
-
{errorMessage &&
|
|
156
|
-
<div className="errorMsg">
|
|
157
|
-
{errorMessage}
|
|
158
|
-
<div style={{textAlign: 'center', fontSize: '0ß.7em', paddingTop: '0.8em'}}><span>Make sure to include the request ID when reporting a problem: {requestId}</span>
|
|
159
|
-
</div>
|
|
164
|
+
onClick={handlers.onShowStart}>
|
|
165
|
+
<div className="icIcon"></div>
|
|
166
|
+
<div className="textDesc"> Try a different image</div>
|
|
167
|
+
<br style={{ clear: "both" }} />
|
|
160
168
|
</div>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<section
|
|
172
|
+
className={classNames('results', {resultsActive: showPart === 'results'}, (results.length === 1 ? 'singleProduct' : 'multipleProducts'))}>
|
|
173
|
+
{errorMessage &&
|
|
174
|
+
<div className="errorMsg">
|
|
175
|
+
{errorMessage}
|
|
176
|
+
<div style={{textAlign: 'center', fontSize: '0.7em', paddingTop: '0.8em'}}><span>Make sure to include the request ID when reporting a problem: {requestId}</span>
|
|
167
177
|
</div>
|
|
178
|
+
</div>
|
|
168
179
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
translateX: [0],
|
|
194
|
-
timing: {delay: i * 100, duration: 300}
|
|
195
|
-
})}
|
|
196
|
-
>
|
|
197
|
-
{rs => <>{rs.map(({key, data, state}) => <Result
|
|
198
|
-
key={key}
|
|
199
|
-
noImageUrl={settings.noImageUrl}
|
|
200
|
-
template={settings.resultTemplate}
|
|
201
|
-
onImageClick={handlers.onImageClick}
|
|
202
|
-
onLinkClick={handlers.onLinkClick}
|
|
203
|
-
result={data}
|
|
204
|
-
style={{opacity: state.opacity, transform: `translateX(${state.translateX}%)`}}/>)}</>}
|
|
205
|
-
</NodeGroup>
|
|
180
|
+
<Animate show={loading} start={{opacity: 0.0}} enter={{opacity: [1.0], timing: {duration: 300}}}
|
|
181
|
+
leave={{opacity: [0.0], timing: {duration: 300}}}>
|
|
182
|
+
{s =>
|
|
183
|
+
<div className="loadingOverlay" style={{...s}}>
|
|
184
|
+
<div className="loading"/>
|
|
185
|
+
</div>
|
|
186
|
+
}
|
|
187
|
+
</Animate>
|
|
188
|
+
{settings.preview && previewImage &&
|
|
189
|
+
<div className="preview">
|
|
190
|
+
<Preview key={previewImage.id}
|
|
191
|
+
maxWidth={document.body.clientWidth} maxHeight={maxPreviewHeight}
|
|
192
|
+
dotColor="#4C8F9F"
|
|
193
|
+
onSelectionChange={handlers.onSelectionChange} regions={regions}
|
|
194
|
+
selection={previewSelection} image={previewImage.canvas}/>
|
|
195
|
+
</div>
|
|
196
|
+
}
|
|
197
|
+
<div className="predicted-categories">
|
|
198
|
+
<PredictedCategories cs={categoryPredictions}/>
|
|
199
|
+
</div>
|
|
200
|
+
<div className="predicted-categories">
|
|
201
|
+
<Codes codes={codes}/>
|
|
202
|
+
</div>
|
|
203
|
+
<CategoryFilter cats={filterOptions}/>
|
|
206
204
|
|
|
207
|
-
|
|
205
|
+
<div className="wrapper">
|
|
206
|
+
<NodeGroup data={results}
|
|
207
|
+
keyAccessor={r => r.sku}
|
|
208
|
+
start={(r, i) => ({opacity: 0, translateX: -100})}
|
|
209
|
+
enter={(r, i) => ({
|
|
210
|
+
opacity: [1],
|
|
211
|
+
translateX: [0],
|
|
212
|
+
timing: {delay: i * 100, duration: 300}
|
|
213
|
+
})}
|
|
214
|
+
>
|
|
215
|
+
{rs => <>{rs.map(({key, data, state}) => <Result
|
|
216
|
+
key={key}
|
|
217
|
+
noImageUrl={settings.noImageUrl}
|
|
218
|
+
template={settings.resultTemplate}
|
|
219
|
+
onImageClick={handlers.onImageClick}
|
|
220
|
+
onLinkClick={handlers.onLinkClick}
|
|
221
|
+
result={data}
|
|
222
|
+
style={{opacity: state.opacity, transform: `translateX(${state.translateX}%)`}}/>)}</>}
|
|
223
|
+
</NodeGroup>
|
|
208
224
|
|
|
209
|
-
|
|
210
|
-
aria-label="sad face">😕</span></div>
|
|
211
|
-
)}
|
|
225
|
+
{results.length === 0 && showPart === 'results' && !loading && (
|
|
212
226
|
|
|
213
|
-
|
|
227
|
+
<div className="noResults">We did not find anything <span role="img"
|
|
228
|
+
aria-label="sad face">😕</span></div>
|
|
229
|
+
)}
|
|
214
230
|
|
|
215
|
-
|
|
216
|
-
took {duration.toFixed(2)} seconds</div>)}
|
|
231
|
+
<br style={{clear: 'both'}}/>
|
|
217
232
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
{duration && showPart === 'results' && (<div style={{textAlign: 'center', fontSize: '0.7em', paddingTop: '0.8em'}}>Search
|
|
234
|
+
took {duration.toFixed(2)} seconds</div>)}
|
|
235
|
+
|
|
236
|
+
{requestId && showPart === 'results' && <div style={{textAlign: 'center', fontSize: '0.7em', paddingTop: '0.8em'}}>Request
|
|
237
|
+
identifier {requestId}</div>}
|
|
238
|
+
</div>
|
|
239
|
+
</section>
|
|
240
|
+
|
|
241
|
+
<Snackbar open={toastOpen} autoHideDuration={3000} onClose={() => setToastOpen(false)}>
|
|
242
|
+
<Alert onClose={() => setToastOpen(false)} severity="error">
|
|
243
|
+
{toastErrorMessage}
|
|
244
|
+
</Alert>
|
|
245
|
+
</Snackbar>
|
|
246
|
+
|
|
247
|
+
</div>
|
|
229
248
|
</div>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
</
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
<section className="footnote">
|
|
250
|
+
<div className="wrapper">
|
|
251
|
+
© 2017 - 2019 <a href="https://nyris.io">nyris GmbH</a> - All rights reserved - <a
|
|
252
|
+
href="https://nyris.io/imprint/">Imprint</a>
|
|
253
|
+
</div>
|
|
254
|
+
</section>
|
|
255
|
+
<Feedback feedbackState={feedbackState} onPositiveFeedback={handlers.onPositiveFeedback}
|
|
256
|
+
onNegativeFeedback={handlers.onNegativeFeedback} onClose={handlers.onCloseFeedback}/>
|
|
257
|
+
|
|
240
258
|
</React.Fragment>
|
|
241
259
|
);
|
|
242
|
-
}
|
|
260
|
+
};
|
|
243
261
|
|
|
244
|
-
export default
|
|
262
|
+
export default App;
|
|
@@ -1,76 +1,80 @@
|
|
|
1
|
-
import {AppAction} from "../types";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export type NyrisAppPart = 'start' | 'camera' | 'results';
|
|
5
|
-
export type NyrisFeedbackState = 'hidden' | 'question' | 'positive' | 'negative';
|
|
1
|
+
import { AppAction } from "../types";
|
|
6
2
|
|
|
3
|
+
export type NyrisAppPart = "start" | "camera" | "results";
|
|
4
|
+
export type NyrisFeedbackState =
|
|
5
|
+
| "hidden"
|
|
6
|
+
| "question"
|
|
7
|
+
| "positive"
|
|
8
|
+
| "negative";
|
|
7
9
|
|
|
8
10
|
export interface NyrisAppState {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
showPart: NyrisAppPart;
|
|
12
|
+
feedbackState: NyrisFeedbackState;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export type NyrisAction =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
| { type: "SHOW_START" }
|
|
17
|
+
| { type: "SHOW_CAMERA" }
|
|
18
|
+
| { type: "SHOW_RESULTS" }
|
|
19
|
+
| { type: "SHOW_FEEDBACK" }
|
|
20
|
+
| { type: "HIDE_FEEDBACK" }
|
|
21
|
+
| { type: "RESULT_LINK_CLICKED"; position: number; url: string }
|
|
22
|
+
| { type: "RESULT_IMAGE_CLICKED"; position: number; url: string };
|
|
22
23
|
|
|
23
|
-
export const showCamera = ()
|
|
24
|
-
export const showStart = ()
|
|
25
|
-
export const showResults = ()
|
|
26
|
-
export const showFeedback = ()
|
|
27
|
-
export const hideFeedback = ()
|
|
24
|
+
export const showCamera = (): NyrisAction => ({ type: "SHOW_CAMERA" });
|
|
25
|
+
export const showStart = (): NyrisAction => ({ type: "SHOW_START" });
|
|
26
|
+
export const showResults = (): NyrisAction => ({ type: "SHOW_RESULTS" });
|
|
27
|
+
export const showFeedback = (): NyrisAction => ({ type: "SHOW_FEEDBACK" });
|
|
28
|
+
export const hideFeedback = (): NyrisAction => ({ type: "HIDE_FEEDBACK" });
|
|
28
29
|
|
|
29
|
-
const initialNyrisState
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const initialNyrisState: NyrisAppState = {
|
|
31
|
+
showPart: "start",
|
|
32
|
+
feedbackState: "hidden",
|
|
32
33
|
};
|
|
33
34
|
|
|
34
|
-
export function reducer(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
35
|
+
export function reducer(
|
|
36
|
+
state: NyrisAppState = initialNyrisState,
|
|
37
|
+
action: AppAction
|
|
38
|
+
): NyrisAppState {
|
|
39
|
+
switch (action.type) {
|
|
40
|
+
case "SHOW_START":
|
|
41
|
+
return {
|
|
42
|
+
...state,
|
|
43
|
+
showPart: "start",
|
|
44
|
+
};
|
|
45
|
+
case "SHOW_CAMERA":
|
|
46
|
+
return {
|
|
47
|
+
...state,
|
|
48
|
+
showPart: "camera",
|
|
49
|
+
};
|
|
50
|
+
case "SEARCH_REQUEST_START":
|
|
51
|
+
case "REGION_REQUEST_START":
|
|
52
|
+
case "SHOW_RESULTS":
|
|
53
|
+
return {
|
|
54
|
+
...state,
|
|
55
|
+
showPart: "results",
|
|
56
|
+
};
|
|
57
|
+
case "SHOW_FEEDBACK":
|
|
58
|
+
return {
|
|
59
|
+
...state,
|
|
60
|
+
feedbackState: "question",
|
|
61
|
+
};
|
|
62
|
+
case "HIDE_FEEDBACK":
|
|
63
|
+
return {
|
|
64
|
+
...state,
|
|
65
|
+
feedbackState: "hidden",
|
|
66
|
+
};
|
|
67
|
+
case "FEEDBACK_SUBMIT_POSITIVE":
|
|
68
|
+
return {
|
|
69
|
+
...state,
|
|
70
|
+
feedbackState: "positive",
|
|
71
|
+
};
|
|
72
|
+
case "FEEDBACK_SUBMIT_NEGATIVE":
|
|
73
|
+
return {
|
|
74
|
+
...state,
|
|
75
|
+
feedbackState: "negative",
|
|
76
|
+
};
|
|
77
|
+
default:
|
|
78
|
+
return state;
|
|
79
|
+
}
|
|
76
80
|
}
|