@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.
Files changed (32) hide show
  1. package/build/asset-manifest.json +11 -11
  2. package/build/index.html +1 -1
  3. package/build/js/settings.example.js +30 -0
  4. package/build/{precache-manifest.a97813497ab8d37548141e5e2618d0dc.js → precache-manifest.fa9930c6ba4e9fb0ebad3869127ef308.js} +9 -9
  5. package/build/service-worker.js +1 -1
  6. package/build/static/css/main.6633770c.chunk.css +2 -0
  7. package/build/static/css/main.6633770c.chunk.css.map +1 -0
  8. package/build/static/js/2.0798bb8b.chunk.js +3 -0
  9. package/build/static/js/{2.6e13adbe.chunk.js.LICENSE.txt → 2.0798bb8b.chunk.js.LICENSE.txt} +0 -0
  10. package/build/static/js/2.0798bb8b.chunk.js.map +1 -0
  11. package/build/static/js/main.31212715.chunk.js +2 -0
  12. package/build/static/js/main.31212715.chunk.js.map +1 -0
  13. package/package.json +2 -2
  14. package/public/js/settings.example.js +30 -0
  15. package/src/App.tsx +221 -203
  16. package/src/actions/nyrisAppActions.ts +69 -65
  17. package/src/actions/searchActions.ts +288 -196
  18. package/src/components/FiltersList.tsx +106 -57
  19. package/src/components/Header.tsx +29 -17
  20. package/src/components/SelectedFiltersSummary.tsx +84 -0
  21. package/src/components/Sidebar.tsx +41 -34
  22. package/src/epics/index.ts +128 -108
  23. package/src/epics/search.ts +114 -82
  24. package/src/index.css +95 -6
  25. package/src/index.tsx +89 -95
  26. package/src/utils.ts +5 -0
  27. package/build/static/css/main.0481043c.chunk.css +0 -2
  28. package/build/static/css/main.0481043c.chunk.css.map +0 -1
  29. package/build/static/js/2.6e13adbe.chunk.js +0 -3
  30. package/build/static/js/2.6e13adbe.chunk.js.map +0 -1
  31. package/build/static/js/main.f5da7aa4.chunk.js +0 -2
  32. 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 React, { useEffect, useState } from "react";
2
- import { useDispatch } from "react-redux";
3
- import { Animate, NodeGroup } from "react-move";
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
- Box,
7
- Snackbar
8
- } from "@material-ui/core";
9
- import { Alert } from "@material-ui/lab";
10
- import classNames from "classnames";
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 Feedback from "./components/Feedback";
16
- import CategoryFilter from "./components/CategoryFilter";
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
- search: {
42
- results: any[],
43
- requestId?: string,
44
- duration?: number,
45
- categoryPredictions: CategoryPrediction[],
46
- codes: Code[],
47
- filterOptions: string[],
48
- errorMessage?: string,
49
- regions: Region[],
50
- previewSelection: RectCoords,
51
- toastErrorMessage?: string,
52
- filters: Filter[],
53
- selectedFilters: Map<string,string[]>
54
- },
55
- previewImage?: CanvasWithId,
56
- settings: AppSettings,
57
- loading: boolean,
58
- showPart: NyrisAppPart,
59
- feedbackState: NyrisFeedbackState,
60
- handlers: AppHandlers,
61
- mdSettings: MDSettings
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
- const Demo2: React.FC<AppProps> = ({
65
- search: {results, regions, previewSelection, requestId, duration, errorMessage, filterOptions, categoryPredictions, codes, toastErrorMessage, filters, selectedFilters},
66
- showPart, settings, handlers, loading, previewImage, feedbackState
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
- const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop: (fs: File[]) => handlers.onFileDropped(fs[0])});
70
-
71
- const minPreviewHeight = 400;
72
- const halfOfTheScreenHeight = Math.floor(window.innerHeight * 0.45);
73
- const maxPreviewHeight = Math.max(minPreviewHeight, halfOfTheScreenHeight);
74
- const [toastOpen, setToastOpen] = useState(false);
75
- const dispatch = useDispatch();
76
-
77
- const acceptTypes =[ 'image/*' ].concat( settings.cadSearch ? cadExtensions : [] ).join(',');
78
-
79
-
80
- useEffect(() => {
81
- if (toastErrorMessage !== '') {
82
- setToastOpen(true);
83
- }
84
- }, [toastErrorMessage])
85
-
86
- useEffect(() =>{
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
- }, [showPart, dispatch])
89
-
98
+ }, [showPart, dispatch]);
99
+
90
100
  return (
91
101
  <React.Fragment>
92
- <Header/>
93
- <div className="wrapperBody">
94
- <Sidebar filters={filters} selectedFilters={selectedFilters}/>
95
-
96
- <div className="mainContent">
97
- {showPart === 'camera' &&
98
- <Capture onCaptureComplete={handlers.onCaptureComplete} onCaptureCanceled={handlers.onCaptureCanceled}
99
- useAppText='Use default camera app'/>}
100
-
101
-
102
- <div className={classNames('headSection', {hidden: showPart === 'results'})} id="headSection">
103
- <div {...getRootProps({
104
- onClick: e => {
105
- e.stopPropagation();
106
- }
107
- })} className={classNames('wrapper', 'dragAndDropActionArea', {'fileIsHover': isDragActive})}>
108
-
109
- <div className={classNames('contentWrap')}>
110
- <Box display="flex" flexDirection="column" justifyContent="center" alignItems="center" minHeight="100vh">
111
- <section className="uploadImage">
112
- <input type="button" name="file" id="capture" className="inputfile" accept="image/*"
113
- capture="environment" onClick={handlers.onCameraClick}/>
114
- <input type="file" name="file" id="capture_file" className="inputfile" accept={acceptTypes}
115
- capture="environment"/>
116
- <input {...getInputProps()} type="file" name="file" id="select_file" className="inputfile"
117
- accept={acceptTypes}
118
- onChange={makeFileHandler(handlers.onSelectFile)}
119
- />
120
- <div className="onDesktop">
121
- Drop an image
122
- <div className="smallText">or</div>
123
- </div>
124
- <div className="onMobile camIcon">
125
- <img src="./images/ic_cam_large.svg" alt="Camera"/>
126
- </div>
127
- <label htmlFor="capture" className="btn primary onMobile"
128
- style={{marginBottom: '2em', width: '12em'}}>
129
- <span className="onMobile">Take a picture</span>
130
- </label>
131
- <br/>
132
- <label htmlFor="select_file" className="btn primary" style={{width: '12em'}}>
133
- <span>Select a file</span>
134
- </label>
135
- <label htmlFor="capture" className="mobileUploadHandler onMobile"/>
136
- </section>
137
- <ExampleImages images={settings.exampleImages} onExampleImageClicked={handlers.onExampleImageClick}/>
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
- </div>
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
- onClick={handlers.onShowStart}>
147
- <div className="icIcon">
148
- </div>
149
- <div className="textDesc"> Try a different image</div>
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
- <Animate show={loading} start={{opacity: 0.0}} enter={{opacity: [1.0], timing: {duration: 300}}}
163
- leave={{opacity: [0.0], timing: {duration: 300}}}>
164
- {s =>
165
- <div className="loadingOverlay" style={{...s}}>
166
- <div className="loading"/>
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
- </Animate>
170
- {settings.preview && previewImage &&
171
- <div className="preview">
172
- <Preview key={previewImage.id}
173
- maxWidth={document.body.clientWidth} maxHeight={maxPreviewHeight}
174
- dotColor="#4C8F9F"
175
- onSelectionChange={handlers.onSelectionChange} regions={regions}
176
- selection={previewSelection} image={previewImage.canvas}/>
177
- </div>
178
- }
179
- <div className="predicted-categories">
180
- <PredictedCategories cs={categoryPredictions}/>
181
- </div>
182
- <div className="predicted-categories">
183
- <Codes codes={codes}/>
184
- </div>
185
- <CategoryFilter cats={filterOptions}/>
186
-
187
- <div className="wrapper">
188
- <NodeGroup data={results}
189
- keyAccessor={r => r.sku}
190
- start={(r, i) => ({opacity: 0, translateX: -100})}
191
- enter={(r, i) => ({
192
- opacity: [1],
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
- {results.length === 0 && showPart === 'results' && !loading && (
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
- <div className="noResults">We did not find anything <span role="img"
210
- aria-label="sad face">😕</span></div>
211
- )}
225
+ {results.length === 0 && showPart === 'results' && !loading && (
212
226
 
213
- <br style={{clear: 'both'}}/>
227
+ <div className="noResults">We did not find anything <span role="img"
228
+ aria-label="sad face">😕</span></div>
229
+ )}
214
230
 
215
- {duration && showPart === 'results' && (<div style={{textAlign: 'center', fontSize: '0.7em', paddingTop: '0.8em'}}>Search
216
- took {duration.toFixed(2)} seconds</div>)}
231
+ <br style={{clear: 'both'}}/>
217
232
 
218
- {requestId && showPart === 'results' && <div style={{textAlign: 'center', fontSize: '0.7em', paddingTop: '0.8em'}}>Request
219
- identifier {requestId}</div>}
220
- </div>
221
- </section>
222
-
223
- <Snackbar open={toastOpen} autoHideDuration={3000} onClose={() => setToastOpen(false)}>
224
- <Alert onClose={() => setToastOpen(false)} severity="error">
225
- {toastErrorMessage}
226
- </Alert>
227
- </Snackbar>
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
- </div>
231
- <section className="footnote">
232
- <div className="wrapper">
233
- © 2017 - 2019 <a href="https://nyris.io">nyris GmbH</a> - All rights reserved - <a
234
- href="https://nyris.io/imprint/">Imprint</a>
235
- </div>
236
- </section>
237
- <Feedback feedbackState={feedbackState} onPositiveFeedback={handlers.onPositiveFeedback}
238
- onNegativeFeedback={handlers.onNegativeFeedback} onClose={handlers.onCloseFeedback}/>
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 Demo2;
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
- showPart: NyrisAppPart,
10
- feedbackState: NyrisFeedbackState
11
+ showPart: NyrisAppPart;
12
+ feedbackState: NyrisFeedbackState;
11
13
  }
12
14
 
13
15
  export type NyrisAction =
14
- | { type: 'SHOW_START' }
15
- | { type: 'SHOW_CAMERA' }
16
- | { type: 'SHOW_RESULTS' }
17
- | { type: 'SHOW_FEEDBACK' }
18
- | { type: 'HIDE_FEEDBACK' }
19
- | { type: 'RESULT_LINK_CLICKED', position: number, url: string}
20
- | { type: 'RESULT_IMAGE_CLICKED', position: number, url: string}
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 = () :NyrisAction => ({type: 'SHOW_CAMERA'});
24
- export const showStart = () :NyrisAction => ({type: 'SHOW_START'});
25
- export const showResults = () :NyrisAction => ({type: 'SHOW_RESULTS'});
26
- export const showFeedback = () :NyrisAction => ({type: 'SHOW_FEEDBACK'});
27
- export const hideFeedback = () :NyrisAction => ({type: 'HIDE_FEEDBACK'});
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 : NyrisAppState = {
30
- showPart: 'start',
31
- feedbackState: 'hidden'
30
+ const initialNyrisState: NyrisAppState = {
31
+ showPart: "start",
32
+ feedbackState: "hidden",
32
33
  };
33
34
 
34
- export function reducer(state : NyrisAppState = initialNyrisState, action: AppAction) : NyrisAppState {
35
- switch (action.type) {
36
- case 'SHOW_START':
37
- return {
38
- ...state,
39
- showPart: 'start'
40
- };
41
- case 'SHOW_CAMERA':
42
- return {
43
- ...state,
44
- showPart: 'camera'
45
- };
46
- case 'SEARCH_REQUEST_START':
47
- case 'REGION_REQUEST_START':
48
- case 'SHOW_RESULTS':
49
- return {
50
- ...state,
51
- showPart: 'results'
52
- };
53
- case 'SHOW_FEEDBACK':
54
- return {
55
- ...state,
56
- feedbackState: 'question'
57
- };
58
- case 'HIDE_FEEDBACK':
59
- return {
60
- ...state,
61
- feedbackState: 'hidden'
62
- };
63
- case 'FEEDBACK_SUBMIT_POSITIVE':
64
- return {
65
- ...state,
66
- feedbackState: 'positive'
67
- };
68
- case 'FEEDBACK_SUBMIT_NEGATIVE':
69
- return {
70
- ...state,
71
- feedbackState: 'negative'
72
- };
73
- default:
74
- return state;
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
  }