@nyris/nyris-webapp 0.3.66 → 0.3.68

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.
@@ -0,0 +1,179 @@
1
+ import { RectCoords } from '@nyris/nyris-api';
2
+ import { isEmpty } from 'lodash';
3
+ import { useCallback } from 'react';
4
+ import { findCad, findRegions, getRequestImage } from 'services/image';
5
+ import useRequestStore from 'Store/requestStore';
6
+ import useResultStore from 'Store/resultStore';
7
+ import {
8
+ setFirstSearchImage,
9
+ setFirstSearchPrefilters,
10
+ setFirstSearchResults,
11
+ setRegions,
12
+ setRequestImage,
13
+ setSearchResults,
14
+ setSelectedRegion,
15
+ setShowFeedback,
16
+ updateStatusLoading,
17
+ } from 'Store/search/Search';
18
+ import { useAppDispatch, useAppSelector } from 'Store/Store';
19
+ import { AppSettings } from 'types';
20
+
21
+ export const useCadSearch = () => {
22
+ const dispatch = useAppDispatch();
23
+ const preFilter = useAppSelector(state => state.search.preFilter);
24
+
25
+ const { setRequestImages, setImageRegions } = useRequestStore(state => ({
26
+ setRequestImages: state.setRequestImages,
27
+ setImageRegions: state.setRegions,
28
+ requestImages: state.requestImages,
29
+ regions: state.regions,
30
+ }));
31
+
32
+ const { setDetectedObject } = useResultStore(state => ({
33
+ setDetectedObject: state.setDetectedObject,
34
+ }));
35
+
36
+ const cadSearch = useCallback(
37
+ async ({
38
+ file,
39
+ settings,
40
+ showFeedback = true,
41
+ imageRegion,
42
+ newSearch,
43
+ }: {
44
+ file: File;
45
+ settings: AppSettings;
46
+ showFeedback?: boolean;
47
+ imageRegion?: RectCoords;
48
+ newSearch?: boolean;
49
+ }) => {
50
+ let res: any;
51
+
52
+ const preFilterValues = [
53
+ {
54
+ key: settings.visualSearchFilterKey,
55
+ values: Object.keys(preFilter),
56
+ },
57
+ ];
58
+ let filters: any[] = [];
59
+ // const canvas = document.createElement('canvas');
60
+
61
+ // dispatch(setRequestImage(file));
62
+ // setRequestImages([file as unknown as HTMLCanvasElement]);
63
+
64
+ try {
65
+ res = await findCad({
66
+ file,
67
+ settings,
68
+ filters: !isEmpty(preFilter) ? preFilterValues : undefined,
69
+ });
70
+
71
+ res?.responseBody?.results.forEach((item: any) => {
72
+ filters.push({
73
+ sku: item.sku,
74
+ score: item.score,
75
+ });
76
+ });
77
+ const payload = {
78
+ ...res?.responseBody,
79
+ filters,
80
+ };
81
+ dispatch(setSearchResults(payload));
82
+
83
+ const queryParams = res?.requestUrl.split('?')[1];
84
+
85
+ // Parse the query string
86
+ const params = new URLSearchParams(queryParams);
87
+
88
+ // Get the value of the 'img' parameter
89
+ const imgValue = params.get('img');
90
+
91
+ const image = await getRequestImage({
92
+ url: encodeURIComponent(imgValue || ''),
93
+ settings,
94
+ });
95
+
96
+ const blob: Blob = image.data;
97
+
98
+ blobToCanvas(blob)
99
+ .then(async canvas => {
100
+ dispatch(setRequestImage(canvas));
101
+ setRequestImages([canvas]);
102
+ dispatch(setFirstSearchImage(canvas));
103
+
104
+ try {
105
+ let region: RectCoords | undefined = imageRegion;
106
+
107
+ let res = await findRegions(canvas, settings);
108
+ setDetectedObject(res.regions, 0);
109
+ dispatch(setRegions(res.regions));
110
+ region = res.selectedRegion;
111
+ dispatch(setSelectedRegion(region));
112
+ setImageRegions([region]);
113
+ } catch (error) {}
114
+ })
115
+ .catch(error => {
116
+ console.error('Error converting Blob to Canvas:', error);
117
+ });
118
+
119
+ if (showFeedback) {
120
+ dispatch(setShowFeedback(true));
121
+ }
122
+ // go back
123
+ if (newSearch) {
124
+ dispatch(setFirstSearchResults(payload));
125
+ dispatch(setFirstSearchPrefilters(preFilter));
126
+ }
127
+ } catch (error) {
128
+ dispatch(updateStatusLoading(false));
129
+ }
130
+
131
+ return res?.responseBody;
132
+ },
133
+ [dispatch, preFilter, setDetectedObject, setImageRegions, setRequestImages],
134
+ );
135
+
136
+ return { cadSearch };
137
+ };
138
+
139
+ function blobToCanvas(blob: Blob): Promise<HTMLCanvasElement> {
140
+ return new Promise((resolve, reject) => {
141
+ // Step 1: Create a new Image element
142
+ const img = new Image();
143
+
144
+ // Step 2: Create an Object URL from the Blob
145
+ const url = URL.createObjectURL(blob);
146
+ img.src = url;
147
+
148
+ // Step 3: Wait for the image to load
149
+ img.onload = () => {
150
+ // Step 4: Create a Canvas element
151
+ const canvas = document.createElement('canvas');
152
+ const ctx = canvas.getContext('2d');
153
+
154
+ if (!ctx) {
155
+ reject(new Error('Failed to get canvas context'));
156
+ return;
157
+ }
158
+
159
+ // Set canvas dimensions to match the image
160
+ canvas.width = img.width;
161
+ canvas.height = img.height;
162
+
163
+ // Step 5: Draw the image onto the Canvas
164
+ ctx.drawImage(img, 0, 0);
165
+
166
+ // Step 6: Revoke the Object URL to free memory
167
+ URL.revokeObjectURL(url);
168
+
169
+ // Resolve the Canvas element
170
+ resolve(canvas);
171
+ };
172
+
173
+ // Handle errors
174
+ img.onerror = () => {
175
+ URL.revokeObjectURL(url);
176
+ reject(new Error('Failed to load image from Blob'));
177
+ };
178
+ });
179
+ }
@@ -47,6 +47,7 @@ export const useImageSearch = () => {
47
47
  imageRegion,
48
48
  newSearch,
49
49
  compress = true,
50
+ preFilterParams,
50
51
  }: {
51
52
  image: any;
52
53
  settings: AppSettings;
@@ -54,6 +55,7 @@ export const useImageSearch = () => {
54
55
  imageRegion?: RectCoords;
55
56
  newSearch?: boolean;
56
57
  compress?: boolean;
58
+ preFilterParams?: Record<string, boolean>;
57
59
  }) => {
58
60
  let region: RectCoords | undefined = imageRegion;
59
61
  let res: any;
@@ -88,7 +90,7 @@ export const useImageSearch = () => {
88
90
  const preFilterValues = [
89
91
  {
90
92
  key: settings.visualSearchFilterKey,
91
- values: Object.keys(preFilter),
93
+ values: Object.keys(preFilterParams || preFilter),
92
94
  },
93
95
  ];
94
96
  let filters: any[] = [];
@@ -97,7 +99,9 @@ export const useImageSearch = () => {
97
99
  res = await find({
98
100
  image: requestImage,
99
101
  settings,
100
- filters: !isEmpty(preFilter) ? preFilterValues : undefined,
102
+ filters: !isEmpty(preFilterParams || preFilter)
103
+ ? preFilterValues
104
+ : undefined,
101
105
  region,
102
106
  });
103
107
 
@@ -145,16 +149,18 @@ export const useImageSearch = () => {
145
149
  settings,
146
150
  regions,
147
151
  showFeedback = true,
152
+ preFilterParams,
148
153
  }: {
149
154
  images: HTMLCanvasElement[];
150
155
  regions: RectCoords[];
151
156
  settings: AppSettings;
152
157
  showFeedback?: boolean;
158
+ preFilterParams?: Record<string, boolean>;
153
159
  }) => {
154
160
  const preFilterValues = [
155
161
  {
156
162
  key: settings.visualSearchFilterKey,
157
- values: Object.keys(preFilter),
163
+ values: Object.keys(preFilterParams || preFilter),
158
164
  },
159
165
  ];
160
166
  let filters: any[] = [];
@@ -164,7 +170,9 @@ export const useImageSearch = () => {
164
170
  images,
165
171
  settings,
166
172
  regions,
167
- filters: !isEmpty(preFilter) ? preFilterValues : undefined,
173
+ filters: !isEmpty(preFilterParams || preFilter)
174
+ ? preFilterValues
175
+ : undefined,
168
176
  });
169
177
 
170
178
  res?.results.forEach((item: any) => {
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import 'react-app-polyfill/ie11';
3
3
  import 'react-app-polyfill/stable';
4
4
  import 'typeface-roboto';
@@ -6,9 +6,16 @@ import 'index.css';
6
6
 
7
7
  import AppMD from 'page/landingPage/HomeDesktop';
8
8
  import AppMobile from 'page/landingPage/HomeMobile';
9
- // import { useAppSelector } from 'Store/Store';
9
+ import { useAppSelector } from 'Store/Store';
10
10
 
11
11
  function Home(): JSX.Element {
12
+ const experienceVisualSearch = useAppSelector(
13
+ state => state.settings.experienceVisualSearch,
14
+ );
15
+ const experienceVisualSearchImages = useAppSelector(
16
+ state => state.settings.experienceVisualSearchImages,
17
+ );
18
+
12
19
  // const clarityId = useAppSelector(state => state.settings.clarityId);
13
20
 
14
21
  // useEffect(() => {
@@ -38,10 +45,46 @@ function Home(): JSX.Element {
38
45
  // }
39
46
  // };
40
47
 
48
+ const [experienceVisualSearchBlobs, setExperienceVisualSearchBlobs] =
49
+ useState<Blob[]>([]);
50
+
51
+ const fetchImage = async (url: string) => {
52
+ const response = await fetch(url, { cache: 'force-cache' });
53
+ const blob = await response.blob();
54
+ return blob;
55
+ };
56
+
57
+ useEffect(() => {
58
+ const fetchAllImages = async () => {
59
+ if (experienceVisualSearch && experienceVisualSearchImages?.length) {
60
+ const randomImages = experienceVisualSearchImages?.slice(
61
+ 0,
62
+ Math.min(experienceVisualSearchImages?.length, 4),
63
+ );
64
+ try {
65
+ // randomImages.forEach(url => {
66
+ // fetchImage(url).then(value => {
67
+ // setExperienceVisualSearchBlobs(s => [...s, value]);
68
+ // });
69
+ // });
70
+
71
+ const responses = await Promise.all(
72
+ randomImages.map(url => fetchImage(url)),
73
+ );
74
+ setExperienceVisualSearchBlobs(responses);
75
+ } catch (error) {
76
+ console.error('Error fetching images:', error);
77
+ }
78
+ }
79
+ };
80
+
81
+ fetchAllImages();
82
+ }, [experienceVisualSearch, experienceVisualSearchImages]);
83
+
41
84
  return (
42
85
  <>
43
- <AppMobile />
44
- <AppMD />
86
+ <AppMobile experienceVisualSearchBlobs={experienceVisualSearchBlobs} />
87
+ <AppMD experienceVisualSearchBlobs={experienceVisualSearchBlobs} />
45
88
  </>
46
89
  );
47
90
  }
@@ -8,7 +8,11 @@ import { connectInfiniteHits } from 'react-instantsearch-dom';
8
8
  import { useAppSelector } from 'Store/Store';
9
9
  import { AlgoliaSettings } from '../../types';
10
10
 
11
- function AppMD() {
11
+ function AppMD({
12
+ experienceVisualSearchBlobs,
13
+ }: {
14
+ experienceVisualSearchBlobs: Blob[];
15
+ }) {
12
16
  const { settings } = useAppSelector(state => state);
13
17
  const [isLoading, setLoading] = useState<boolean>(false);
14
18
  const { apiKey, appId, indexName } = settings.algolia as AlgoliaSettings;
@@ -54,7 +58,13 @@ function AppMD() {
54
58
  isLoading={isLoading}
55
59
  onChangeLoading={onChangeLoading}
56
60
  />
57
- {settings.experienceVisualSearch ? <ExperienceVisualSearch /> : ''}
61
+ {settings.experienceVisualSearch ? (
62
+ <ExperienceVisualSearch
63
+ experienceVisualSearchBlobs={experienceVisualSearchBlobs}
64
+ />
65
+ ) : (
66
+ ''
67
+ )}
58
68
  </div>
59
69
  </div>
60
70
  );
@@ -6,7 +6,11 @@ import { useAppSelector } from 'Store/Store';
6
6
  import ExperienceVisualSearch from '../../components/Experience-visual-search/ExperienceVisualSearch';
7
7
  import { Icon } from '@nyris/nyris-react-components';
8
8
 
9
- function AppMobile(): JSX.Element {
9
+ function AppMobile({
10
+ experienceVisualSearchBlobs,
11
+ }: {
12
+ experienceVisualSearchBlobs: Blob[];
13
+ }): JSX.Element {
10
14
  const { settings } = useAppSelector(state => state);
11
15
  const [isOpenModalCamera, setOpenModalCamera] = useState<boolean>(false);
12
16
 
@@ -48,7 +52,13 @@ function AppMobile(): JSX.Element {
48
52
  }}
49
53
  />
50
54
  </div>
51
- {settings.experienceVisualSearch ? <ExperienceVisualSearch /> : ''}
55
+ {settings.experienceVisualSearch ? (
56
+ <ExperienceVisualSearch
57
+ experienceVisualSearchBlobs={experienceVisualSearchBlobs}
58
+ />
59
+ ) : (
60
+ ''
61
+ )}
52
62
  </div>
53
63
  );
54
64
  }
@@ -85,7 +85,7 @@ function ResultComponent(props: Props) {
85
85
  const isPostFilterEnabled = settings.postFilterOption;
86
86
  const history = useHistory();
87
87
 
88
- const { singleImageSearch, multiImageSearch } = useImageSearch();
88
+ const { singleImageSearch } = useImageSearch();
89
89
 
90
90
  const { resetRegions, imageRegions, requestImages } = useRequestStore(
91
91
  state => ({
@@ -188,35 +188,6 @@ function ResultComponent(props: Props) {
188
188
  setFilterString(filter);
189
189
  }, [preFilter, requestImage, searchQuery, settings.alogoliaFilterField]);
190
190
 
191
- useEffect(() => {
192
- if (requestImages.length === 0 || !isAlgoliaEnabled) {
193
- return;
194
- }
195
- dispatch(updateStatusLoading(true));
196
- dispatch(loadingActionResults());
197
- if (requestImages.length === 1) {
198
- singleImageSearch({
199
- image: requestImages[0],
200
- settings,
201
- imageRegion: imageRegions[0],
202
- compress: false,
203
- }).then(res => {
204
- dispatch(updateStatusLoading(false));
205
- });
206
- } else {
207
- multiImageSearch({
208
- images: requestImages,
209
- settings,
210
- regions: imageRegions,
211
- }).then(res => {
212
- dispatch(updateStatusLoading(false));
213
- });
214
- }
215
-
216
- return () => {};
217
- // eslint-disable-next-line react-hooks/exhaustive-deps
218
- }, [preFilter]);
219
-
220
191
  useEffect(() => {
221
192
  if (!requestImage) return;
222
193
 
@@ -89,3 +89,31 @@ export const findMulti = ({
89
89
 
90
90
  return nyrisApi.findMulti(options, images, regions, filters);
91
91
  };
92
+
93
+ export const findCad = ({
94
+ file,
95
+ options,
96
+ settings,
97
+ filters,
98
+ }: {
99
+ file: File;
100
+ settings: NyrisAPISettings;
101
+ options?: ImageSearchOptions;
102
+ filters?: Filter[];
103
+ }) => {
104
+ const nyrisApi = new NyrisAPI(settings);
105
+
106
+ return nyrisApi.findByCad(file, {}, filters);
107
+ };
108
+
109
+ export const getRequestImage = ({
110
+ url,
111
+ settings,
112
+ }: {
113
+ url: string;
114
+ settings: NyrisAPISettings;
115
+ }) => {
116
+ const nyrisApi = new NyrisAPI(settings);
117
+
118
+ return nyrisApi.getCadRequestImage(url);
119
+ };
package/src/types.ts CHANGED
@@ -36,33 +36,33 @@ export interface Field {
36
36
  productDetails: string;
37
37
  }
38
38
  interface CTAButtonSettings {
39
- CTAButton?: boolean,
40
- CTAButtonText?: string,
41
- CTAButtonTextColor?: string,
42
- CTAButtonColor?: string,
43
- CTAIcon?: boolean,
44
- CTALinkField?: string,
39
+ CTAButton?: boolean;
40
+ CTAButtonText?: string;
41
+ CTAButtonTextColor?: string;
42
+ CTAButtonColor?: string;
43
+ CTAIcon?: boolean;
44
+ CTALinkField?: string;
45
45
  }
46
46
 
47
47
  interface SecondaryCTAButton {
48
- secondaryCTAButton?: boolean,
49
- secondaryCTAButtonText?: string,
50
- secondaryCTAButtonTextColor?: string,
51
- secondaryCTAButtonColor?: string,
52
- secondaryCTAIcon?: boolean,
53
- secondaryCTALinkField?: string,
48
+ secondaryCTAButton?: boolean;
49
+ secondaryCTAButtonText?: string;
50
+ secondaryCTAButtonTextColor?: string;
51
+ secondaryCTAButtonColor?: string;
52
+ secondaryCTAIcon?: boolean;
53
+ secondaryCTALinkField?: string;
54
54
  }
55
55
 
56
56
  interface Attributes {
57
- productAttributes?: boolean,
58
- attributeOneLabelValue?: string,
59
- attributeOneValue?: string,
60
- attributeTwoLabelValue?: string,
61
- attributeTwoValue?: string,
62
- attributeThreeLabelValue?: string,
63
- attributeThreeValue?: string,
64
- attributeFourLabelValue?: string,
65
- attributeFourValue?: string,
57
+ productAttributes?: boolean;
58
+ attributeOneLabelValue?: string;
59
+ attributeOneValue?: string;
60
+ attributeTwoLabelValue?: string;
61
+ attributeTwoValue?: string;
62
+ attributeThreeLabelValue?: string;
63
+ attributeThreeValue?: string;
64
+ attributeFourLabelValue?: string;
65
+ attributeFourValue?: string;
66
66
  }
67
67
 
68
68
  export interface AppSettings extends NyrisAPISettings {
@@ -73,9 +73,9 @@ export interface AppSettings extends NyrisAPISettings {
73
73
  brandName?: string;
74
74
  cadenas?: Cadenas;
75
75
  clarityId?: string;
76
- mainTitle: string,
76
+ mainTitle: string;
77
77
  productDetails: string;
78
- secondaryTitle: string,
78
+ secondaryTitle: string;
79
79
  CTAButton?: CTAButtonSettings;
80
80
  secondaryCTAButton?: SecondaryCTAButton;
81
81
  attributes?: Attributes;
@@ -92,6 +92,7 @@ export interface AppSettings extends NyrisAPISettings {
92
92
  preFilterOption?: boolean;
93
93
  preFilterTitle?: string;
94
94
  preview: boolean;
95
+ cadSearch?: boolean;
95
96
  refinements?: any;
96
97
  regions: boolean;
97
98
  rfq?: Rfq;
@@ -103,7 +104,7 @@ export interface AppSettings extends NyrisAPISettings {
103
104
  showGroup?: boolean;
104
105
  showPoweredByNyris?: boolean;
105
106
  simpleCardView?: boolean;
106
- support?: Support;
107
+ support: Support;
107
108
  theme: SearchSuiteSettings;
108
109
  visualSearchFilterKey?: string;
109
110
  }
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "singleQuote": true,
3
- "useTabs": false,
4
- "trailingComma": "all",
5
- "proseWrap": "never",
6
- "arrowParens": "avoid"
7
- }