Skip to content

Commit a34aa27

Browse files
Be sure to not register again asset with same url, create a-assets if missing (#824)
* Be sure to not register again asset with same url, create a-assets if missing * Show error when id is already taken by another asset or entity * Show 'Texture already loaded' similar to the other errors * Use Name instead of id in error message
1 parent eb140c1 commit a34aa27

4 files changed

Lines changed: 119 additions & 129 deletions

File tree

src/components/modals/ModalTextures.js

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,12 @@ import { faSearch } from '@fortawesome/free-solid-svg-icons';
44
import { AwesomeIcon } from '../AwesomeIcon';
55
import Events from '../../lib/Events';
66
import Modal from './Modal';
7-
import { insertNewAsset } from '../../lib/assetsUtils';
8-
9-
function getFilename(url, converted = false) {
10-
var filename = url.split('/').pop();
11-
if (converted) {
12-
filename = getValidId(filename);
13-
}
14-
return filename;
15-
}
16-
17-
function isValidId(id) {
18-
// The correct re should include : and . but A-frame seems to fail while accessing them
19-
var re = /^[A-Za-z]+[\w-]*$/;
20-
return re.test(id);
21-
}
22-
23-
function getValidId(name) {
24-
// info.name.replace(/\.[^/.]+$/, '').replace(/\s+/g, '')
25-
return name
26-
.split('.')
27-
.shift()
28-
.replace(/\s/, '-')
29-
.replace(/^\d+\s*/, '')
30-
.replace(/[\W]/, '')
31-
.toLowerCase();
32-
}
7+
import {
8+
getFilename,
9+
getIdFromUrl,
10+
insertNewAsset,
11+
isValidId
12+
} from '../../lib/assetsUtils';
3313

3414
export default class ModalTextures extends React.Component {
3515
static propTypes = {
@@ -158,15 +138,20 @@ export default class ModalTextures extends React.Component {
158138
var self = this;
159139
function onImageLoaded(img) {
160140
var src = self.preview.current.src;
141+
var name = getFilename(src, true);
142+
var existingAssetId = getIdFromUrl(src);
143+
if (existingAssetId) {
144+
name = existingAssetId;
145+
}
161146
self.setState({
162147
preview: {
163148
width: self.preview.current.naturalWidth,
164149
height: self.preview.current.naturalHeight,
165150
src: src,
166151
id: '',
167-
name: getFilename(src, true),
152+
name: name,
168153
filename: getFilename(src),
169-
type: 'new',
154+
type: existingAssetId ? 'asset' : 'new',
170155
loaded: true,
171156
value: 'url(' + src + ')'
172157
}
@@ -179,12 +164,6 @@ export default class ModalTextures extends React.Component {
179164
this.imageName.current.focus();
180165
};
181166

182-
onNameKeyUp = (event) => {
183-
if (event.keyCode === 13 && this.isValidAsset()) {
184-
this.addNewAsset();
185-
}
186-
};
187-
188167
onNameChanged = (event) => {
189168
var state = this.state.preview;
190169
state.name = event.target.value;
@@ -193,6 +172,7 @@ export default class ModalTextures extends React.Component {
193172

194173
toggleNewDialog = () => {
195174
this.setState({ addNewDialogOpened: !this.state.addNewDialogOpened });
175+
this.clear();
196176
};
197177

198178
clear() {
@@ -216,23 +196,19 @@ export default class ModalTextures extends React.Component {
216196
this.setState({ newUrl: e.target.value });
217197
};
218198

219-
isValidAsset() {
220-
let validUrl = isValidId(this.state.preview.name);
221-
let validAsset = this.state.preview.loaded && validUrl;
222-
return validAsset;
223-
}
224-
225199
addNewAsset = () => {
226-
var self = this;
200+
if (this.state.preview.type === 'asset') {
201+
return;
202+
}
203+
227204
insertNewAsset(
228205
'img',
229206
this.state.preview.name,
230207
this.state.preview.src,
231-
true,
232-
function () {
233-
self.generateFromAssets();
234-
self.setState({ addNewDialogOpened: false });
235-
self.clear();
208+
() => {
209+
this.generateFromAssets();
210+
this.setState({ addNewDialogOpened: false });
211+
this.clear();
236212
}
237213
);
238214
};
@@ -244,15 +220,20 @@ export default class ModalTextures extends React.Component {
244220
renderRegistryImages() {
245221
var self = this;
246222
let selectSample = function (image) {
223+
let name = getFilename(image.name, true);
224+
const existingAssetId = getIdFromUrl(image.src);
225+
if (existingAssetId) {
226+
name = existingAssetId;
227+
}
247228
self.setState({
248229
preview: {
249230
width: image.width,
250231
height: image.height,
251232
src: image.src,
252233
id: '',
253-
name: getFilename(image.name, true),
234+
name: name,
254235
filename: getFilename(image.src),
255-
type: 'registry',
236+
type: existingAssetId ? 'asset' : 'registry',
256237
loaded: true,
257238
value: 'url(' + image.src + ')'
258239
}
@@ -291,8 +272,14 @@ export default class ModalTextures extends React.Component {
291272
let isOpen = this.state.isOpen;
292273
let preview = this.state.preview;
293274

294-
let validUrl = isValidId(this.state.preview.name);
295-
let validAsset = this.isValidAsset();
275+
let validId = isValidId(this.state.preview.name);
276+
let assetIdTaken =
277+
validId && !!document.getElementById(this.state.preview.name);
278+
let validAsset =
279+
this.state.preview.loaded &&
280+
validId &&
281+
!assetIdTaken &&
282+
this.state.preview.type !== 'asset';
296283

297284
let addNewAssetButton = this.state.addNewDialogOpened
298285
? 'BACK'
@@ -343,13 +330,32 @@ export default class ModalTextures extends React.Component {
343330
<input
344331
ref={this.imageName}
345332
className={
346-
this.state.preview.name.length > 0 && !validUrl ? 'error' : ''
333+
this.state.preview.name.length > 0 &&
334+
(!validId || assetIdTaken)
335+
? 'error'
336+
: ''
347337
}
338+
readOnly={preview.type === 'asset'}
348339
type="text"
349340
value={this.state.preview.name}
350341
onChange={this.onNameChanged}
351-
onKeyUp={this.onNameKeyUp}
342+
onKeyUp={(event) => {
343+
if (event.keyCode === 13 && validAsset) {
344+
this.addNewAsset();
345+
}
346+
}}
352347
/>
348+
{preview.type !== 'asset' && assetIdTaken && (
349+
<div className="iderror">
350+
Name already taken by another asset or entity
351+
</div>
352+
)}
353+
{this.state.preview.name.length > 0 && !validId && (
354+
<div className="iderror">Name is not valid</div>
355+
)}
356+
{preview.type === 'asset' && (
357+
<div className="iderror">Texture already loaded</div>
358+
)}
353359
<img
354360
ref={this.preview}
355361
width="155px"

src/components/widgets/TextureWidget.js

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,7 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import Events from '../../lib/Events';
4-
5-
function getUrlFromId(assetId) {
6-
return (
7-
assetId.length > 1 &&
8-
document.querySelector(assetId) &&
9-
document.querySelector(assetId).getAttribute('src')
10-
);
11-
}
12-
13-
function GetFilename(url) {
14-
if (url) {
15-
var m = url.toString().match(/.*\/(.+?)\./);
16-
if (m && m.length > 1) {
17-
return m[1];
18-
}
19-
}
20-
return '';
21-
}
22-
23-
function insertNewAsset(type, id, src) {
24-
var element = null;
25-
switch (type) {
26-
case 'img':
27-
{
28-
element = document.createElement('img');
29-
element.id = id;
30-
element.src = src;
31-
}
32-
break;
33-
}
34-
if (element) {
35-
document.getElementsByTagName('a-assets')[0].appendChild(element);
36-
}
37-
}
38-
39-
function insertOrGetImageAsset(src) {
40-
var id = GetFilename(src);
41-
// Search for already loaded asset by src
42-
var element = document.querySelector("a-assets > img[src='" + src + "']");
43-
44-
if (element) {
45-
id = element.id;
46-
} else {
47-
// Check if first char of the ID is a number (Non a valid ID)
48-
// In that case a 'i' preffix will be added
49-
if (!isNaN(parseInt(id[0], 10))) {
50-
id = 'i' + id;
51-
}
52-
if (document.getElementById(id)) {
53-
var i = 1;
54-
while (document.getElementById(id + '_' + i)) {
55-
i++;
56-
}
57-
id += '_' + i;
58-
}
59-
insertNewAsset('img', id, src);
60-
}
61-
62-
return id;
63-
}
4+
import { getUrlFromId } from '../../lib/assetsUtils';
645

656
export default class TextureWidget extends React.Component {
667
static propTypes = {
@@ -197,12 +138,8 @@ export default class TextureWidget extends React.Component {
197138
if (!image) {
198139
return;
199140
}
200-
var value = image.value;
201-
if (image.type !== 'asset') {
202-
var assetId = insertOrGetImageAsset(image.src);
203-
value = '#' + assetId;
204-
}
205141

142+
var value = image.value;
206143
if (this.props.onChange) {
207144
this.props.onChange(this.props.name, value);
208145
}

src/lib/assetsUtils.js

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,49 @@
1-
export function insertNewAsset(
2-
type,
3-
id,
4-
src,
5-
anonymousCrossOrigin,
6-
onLoadedCallback
7-
) {
8-
var element = null;
1+
export function getUrlFromId(assetId) {
2+
return (
3+
assetId.length > 1 &&
4+
document.querySelector(assetId) &&
5+
document.querySelector(assetId).getAttribute('src')
6+
);
7+
}
8+
9+
export function getIdFromUrl(url) {
10+
return document.querySelector("a-assets > [src='" + url + "']")?.id;
11+
}
12+
13+
export function getFilename(url, converted = false) {
14+
var filename = url.split('/').pop();
15+
if (converted) {
16+
filename = getValidId(filename);
17+
}
18+
return filename;
19+
}
20+
21+
export function isValidId(id) {
22+
// The correct re should include : and . but A-frame seems to fail while accessing them
23+
var re = /^[A-Za-z]+[\w-]*$/;
24+
return re.test(id);
25+
}
26+
27+
export function getValidId(name) {
28+
// info.name.replace(/\.[^/.]+$/, '').replace(/\s+/g, '')
29+
return name
30+
.split('.')
31+
.shift()
32+
.replace(/\s/, '-')
33+
.replace(/^\d+\s*/, '')
34+
.replace(/[\W]/, '')
35+
.toLowerCase();
36+
}
37+
38+
export function insertNewAsset(type, id, src, onLoadedCallback = undefined) {
39+
let element;
940
switch (type) {
1041
case 'img':
1142
{
1243
element = document.createElement('img');
1344
element.id = id;
1445
element.src = src;
15-
if (anonymousCrossOrigin) {
16-
element.crossOrigin = 'anonymous';
17-
}
46+
element.crossOrigin = 'anonymous';
1847
}
1948
break;
2049
}
@@ -25,6 +54,17 @@ export function insertNewAsset(
2554
onLoadedCallback();
2655
}
2756
};
28-
document.getElementsByTagName('a-assets')[0].appendChild(element);
57+
58+
let assetsEl = document.querySelector('a-assets');
59+
if (!assetsEl) {
60+
assetsEl = document.createElement('a-assets');
61+
var sceneEl = document.querySelector('a-scene');
62+
if (!sceneEl) {
63+
throw new Error('No a-scene element found to append a-assets to');
64+
}
65+
sceneEl.appendChild(assetsEl);
66+
}
67+
68+
assetsEl.appendChild(element);
2969
}
3070
}

src/style/textureModal.styl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@
9494
margin 8px 0
9595
width 144px
9696

97+
.preview .iderror
98+
background #fff
99+
color #a00
100+
margin-bottom 8px
101+
padding 3px 5px
102+
width 148px
103+
97104
.preview button
98105
width 155px
99106

0 commit comments

Comments
 (0)