Skip to content

Commit c17bfa5

Browse files
authored
Merge pull request #39 from BuildFire/ai-seeder
Ai seeder
2 parents 81e928c + 00b6117 commit c17bfa5

File tree

3 files changed

+275
-29
lines changed

3 files changed

+275
-29
lines changed

src/control/content/containers/Content.js

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class Content extends Component {
2020
originalState: {
2121
folders: [],
2222
images: []
23-
}
23+
},
24+
showEmptyState: false
2425
};
2526
}
2627

@@ -61,12 +62,11 @@ class Content extends Component {
6162
height: naturalHeight
6263
})
6364
);
64-
65+
this.setState({showEmptyState: false});
6566
this.setState(
6667
state => {
6768
let { images } = { ...state };
6869
images = [...images, ...newImages];
69-
7070
return { images };
7171
},
7272
() => this.saveWithDelay()
@@ -151,7 +151,7 @@ class Content extends Component {
151151
// }, 250);
152152
this.saveWithDelay();
153153
};
154-
154+
this.setState({showEmptyState: false});
155155
this.setState(
156156
state => {
157157
const { folders } = { ...state };
@@ -329,24 +329,37 @@ class Content extends Component {
329329
);
330330
};
331331

332+
onSaveAIData = (data, reset) => {
333+
if(reset) {
334+
this.setState({ images: data.images, folders: data.folders, showEmptyState: false }, () => {
335+
this.saveWithDelay();
336+
});
337+
} else {
338+
this.setState({ images: [...this.state.images, ...data.images], folders: [...this.state.folders, ...data.folders], showEmptyState: false }, () => {
339+
this.saveWithDelay();
340+
});
341+
}
342+
}
343+
332344
componentDidMount = () => {
333345
const loadData = (err, result) => {
334346
if (err) throw err;
335347
const { images, folders } = result.data;
336-
337348
let originalState = JSON.parse(JSON.stringify({ ...result.data }))
338349

339-
if (images && folders) {
340-
this.setState(() => ({ images, folders, originalState }));
350+
if ((images && images.length) || (folders && folders.length) ) {
351+
this.setState(() => ({ images, folders, originalState, loaded: true }));
352+
}
353+
else {
354+
this.setState(() => ({ showEmptyState: true, loaded: true }));
341355
}
342356
};
343357

344358
this.Datastore.get((error, result) => loadData(error, result));
345359
};
346360

347361
render() {
348-
const { images, folders, folder } = this.state;
349-
362+
const { images, folders, folder, showEmptyState, loaded } = this.state;
350363
return (
351364
<Router history={History}>
352365
<Route
@@ -356,12 +369,15 @@ class Content extends Component {
356369
<Home
357370
images={images}
358371
folders={folders}
372+
loaded={loaded}
373+
showEmptyState={showEmptyState}
359374
removeImage={this.removeImage}
360375
addFolder={this.addFolder}
361376
removeFolder={this.removeFolder}
362377
openFolder={this.openFolder}
363378
showImageDialog={this.showImageDialog}
364379
handleReorder={this.handleReorder}
380+
saveAIData={this.onSaveAIData}
365381
/>
366382
)}
367383
/>
Lines changed: 249 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,262 @@
11
import React from 'react';
22
import { ImageList, FolderList } from '.';
3+
import { Folder, Img } from '../scripts';
34

45
const Home = ({
56
images,
67
removeImage,
78
folders,
9+
loaded,
10+
showEmptyState,
811
showImageDialog,
912
addFolder,
1013
removeFolder,
1114
openFolder,
12-
handleReorder
13-
}) => (
14-
<>
15-
<ImageList
16-
type="gallery"
17-
fid="gallery-image-list"
18-
images={images}
19-
removeImage={removeImage}
20-
showImageDialog={showImageDialog}
21-
handleReorder={handleReorder}
22-
/>
23-
<FolderList
24-
folders={folders}
25-
addFolder={addFolder}
26-
removeFolder={removeFolder}
27-
openFolder={openFolder}
28-
handleReorder={handleReorder}
29-
/>
30-
</>
31-
);
15+
handleReorder,
16+
saveAIData
17+
}) => {
18+
let imageList = [];
19+
// init state seeder
20+
if (loaded && showEmptyState) {
21+
const generateRandomNumber = () => {
22+
const min = 1000000000000; // Smallest 13-digit number
23+
const max = 9999999999999; // Largest 13-digit number
24+
return Math.floor(Math.random() * (max - min + 1)) + min;
25+
};
26+
27+
/** UTILS */
28+
const elimanateNotFoundImages = (url) => {
29+
const optimisedURL = url.replace('1080x720', '100x100');
30+
return new Promise((resolve) => {
31+
if (url.includes("http")){
32+
const xhr = new XMLHttpRequest();
33+
xhr.open("GET", optimisedURL);
34+
xhr.onerror = (error) => {
35+
console.warn('provided URL is not a valid image', error);
36+
resolve({isValid: false, newURL: null});
37+
}
38+
xhr.onload = () => {
39+
if (xhr.responseURL.includes('source-404') || xhr.status == 404) {
40+
return resolve({isValid: false ,newURL: null});
41+
} else {
42+
return resolve({isValid: true, newURL: xhr.responseURL.replace('h=100', 'h=720').replace('w=100', 'w=1080') });
43+
}
44+
};
45+
xhr.send();
46+
} else resolve(false);
47+
});
48+
};
49+
/** UTILS END */
50+
51+
const seeder = new buildfire.components.aiStateSeeder({
52+
generateOptions: {
53+
userMessage: 'Provide me with images that relate to [Traveling]',
54+
systemMessage: 'Use source.unsplash.com for image URLs, image should be 1080x720, use different image for each entry, URL should not have premium_photo or source.unsplash.com/random. Do not remove white spaces in the subject string.',
55+
hintText: 'Replace values between brackets to match your requirements.',
56+
maxRecords: 10,
57+
jsonTemplate: {
58+
images: [
59+
{
60+
url:'',
61+
topic: ''
62+
}
63+
],
64+
topic: '',
65+
},
66+
callback: (err, result) => {
67+
if (
68+
err ||
69+
!result ||
70+
typeof result !== "object" ||
71+
!Object.keys(result).length || !result.data || !result.data.images || !result.data.images.length
72+
) {
73+
seeder.requestResult.complete();
74+
return buildfire.dialog.toast({
75+
message: "Bad AI request, please try changing your request.",
76+
type: "danger",
77+
});
78+
}
79+
imageList = result.data.images;
80+
if (!imageList.length) {
81+
seeder.requestResult.complete();
82+
return buildfire.dialog.toast({
83+
message: "Bad AI request, please try changing your request.",
84+
type: "danger",
85+
});
86+
}
87+
const promises = imageList.map(
88+
item =>
89+
{
90+
return new Promise((resolve, reject) => {
91+
elimanateNotFoundImages(item.url).then(result => {
92+
if (result.isValid) {
93+
resolve(result.newURL + `?v=${generateRandomNumber()}` );
94+
} else {
95+
reject('image URL is not valid');
96+
}
97+
})
98+
})
99+
}
100+
);
101+
if (!promises.length){
102+
seeder.requestResult.complete();
103+
return buildfire.dialog.toast({
104+
message: "Bad AI request, please try changing your request.",
105+
type: "danger",
106+
});
107+
}
108+
109+
Promise.allSettled(promises).then(newURLs => {
110+
let processedImages = newURLs.map(
111+
(result) => {
112+
if (result.status == 'fulfilled') {
113+
return new Img({
114+
src: result.value,
115+
width: 1080,
116+
height: 720,
117+
})
118+
}
119+
}).filter(e => e);
120+
if (!processedImages.length) {
121+
seeder.requestResult.complete();
122+
return buildfire.dialog.toast({
123+
message: "Bad AI request, please try changing your request.",
124+
type: "danger",
125+
});
126+
}
127+
128+
const processedFolders = [
129+
new Folder({ name: result.data.topic, images: processedImages })
130+
];
131+
const data = {
132+
images: processedImages,
133+
folders: processedFolders,
134+
};
135+
136+
saveAIData(data, true);
137+
seeder.requestResult.complete();
138+
});
139+
},
140+
},
141+
importOptions: {
142+
jsonTemplate: {
143+
folders: [{ name: '', images: [{ url: '' }] }]
144+
},
145+
sampleCSV: 'Weather, https://source.unsplash.com/featured/?sunny\n'
146+
+ 'Weather, https://source.unsplash.com/featured/?rainy\n'
147+
+ 'City, https://source.unsplash.com/featured/?street',
148+
systemMessage: 'Each row contains a folder name and image URL that should belong to. if nothing provided or no URLs provided then return null',
149+
maxRecords: 5,
150+
hintText: 'You may repeat a folder name on multiple lines to assign multiple images to the same folder.',
151+
callback: (err, result) => {
152+
if (
153+
err ||
154+
!result ||
155+
typeof result !== "object" ||
156+
!Object.keys(result).length || !result.data || !result.data.folders || !result.data.folders.length
157+
) {
158+
return buildfire.dialog.toast({
159+
message: "Bad AI request, please try changing your request.",
160+
type: "danger",
161+
});
162+
}
163+
const promises = result.data.folders.reduce(
164+
(accumulator, currentValue) => {
165+
currentValue.images.forEach(i => {
166+
accumulator.push(new Promise((resolve, reject) => {
167+
elimanateNotFoundImages(i.url).then((res) => {
168+
if (res.isValid) {
169+
const image = new Image();
170+
image.onload = () => resolve(image);
171+
image.src = res.newURL;
172+
image.setAttribute('data-folder', currentValue.name);
173+
image.originalSrc = res.newURL;
174+
} else {
175+
reject('image URL is not valid');
176+
}
177+
})
178+
}));
179+
});
180+
return accumulator;
181+
},
182+
[],
183+
);
184+
185+
Promise.allSettled(promises).then(items => {
186+
const processedFolders = [];
187+
const processedImages = [];
188+
if (!items.length){
189+
seeder.requestResult.complete();
190+
return buildfire.dialog.toast({
191+
message: "Bad AI request, please try changing your request.",
192+
type: "danger",
193+
});
194+
}
195+
items.forEach(item => {
196+
if (item.status == 'fulfilled') {
197+
const image = new Img({
198+
src: item.value.originalSrc,
199+
width: item.value.naturalWidth,
200+
height: item.value.naturalHeight
201+
});
202+
processedImages.push(image);
203+
const folder = processedFolders.find(f => f.name === item.value.getAttribute('data-folder'));
204+
205+
if (!folder) {
206+
processedFolders.push(new Folder({ name: item.value.getAttribute('data-folder'), images: [image] }));
207+
} else {
208+
folder.images.push(image);
209+
}
210+
}
211+
});
212+
213+
if (!processedFolders.length || !processedImages.length) {
214+
seeder.requestResult.complete();
215+
return buildfire.dialog.toast({
216+
message: "Bad AI request, please try changing your request.",
217+
type: "danger",
218+
});
219+
}
220+
221+
const data = { images: processedImages, folders: processedFolders }
222+
223+
if (seeder.requestResult.resetData) {
224+
saveAIData(data, true);
225+
} else {
226+
saveAIData(data, false);
227+
}
228+
seeder.requestResult.complete();
229+
});
230+
}
231+
}
232+
})
233+
.smartShowEmptyState();
234+
}
235+
return (
236+
<>
237+
{loaded ?
238+
<>
239+
{showEmptyState ? <div id='ai-seeder'></div> : <></>}
240+
<ImageList
241+
type="gallery"
242+
fid="gallery-image-list"
243+
images={images}
244+
removeImage={removeImage}
245+
showImageDialog={showImageDialog}
246+
handleReorder={handleReorder}
247+
/>
248+
<FolderList
249+
folders={folders}
250+
addFolder={addFolder}
251+
removeFolder={removeFolder}
252+
openFolder={openFolder}
253+
handleReorder={handleReorder}
254+
/>
255+
</>
256+
: <></>}
257+
258+
</>
259+
)
260+
}
32261

33262
export default Home;

src/control/content/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<link href="../../../../styles/siteIcons.css" rel="stylesheet">
66
<script src="../../../../scripts/buildfire.min.js"></script>
77
<script src="../../../../scripts/sortable.min.js"></script>
8+
<script src="../../../../scripts/buildfire/components/aiStateSeeder/aiStateSeeder.js"></script>
89
</head>
910
<body>
1011
<div class="padded-sm" id="mount"></div>

0 commit comments

Comments
 (0)