Compare commits

..

1 Commits

5 changed files with 74 additions and 14 deletions
+4 -2
View File
@@ -4,8 +4,10 @@
### Bug Fixes ### Bug Fixes
- **Fixed previously added set being re-added when adding an individual minifigure** (Issue #150, branch `bugfix/issue-150`): After adding a set, entering a `fig-` ID and confirming would add the previous set again instead of the minifigure, if user did not reload inbetween. - **Fixed crash when importing sets containing minifigures or parts with no image on Rebrickable** (Issue #149c, branch `bugfix/issue-149c`): Adding or refreshing a set would fail entirely if any minifigure or part had no image URL, with error `Invalid URL '': No scheme supplied`
- `add.js` was creating a second `BrickMinifigureSocket` with its own listeners on the same button and input as `BrickSetSocket`, causing duplicate socket events and cross-socket state confusion - Rebrickable returns an empty string (not `None`) for missing images; normalize empty strings to `None` at the point of ingestion in `rebrickable_minifigure.py` and `individual_minifigure.py`, matching the existing pattern in `rebrickable_set.py`
- Updated `rebrickable_image.py` to treat empty strings the same as `None` throughout, falling back to the configured nil placeholder image
- Note: the originally reported sets could no longer reproduce the crash (images may have since been added on Rebrickable), so this fix is based on asumptions only
- **Fixed deleting a wish with an owner assigned** (Issue #152): Resolved foreign key constraint error when removing a set from the wishlist that had an owner assigned - **Fixed deleting a wish with an owner assigned** (Issue #152): Resolved foreign key constraint error when removing a set from the wishlist that had an owner assigned
- Wish owners are now deleted before the wish itself, respecting the FK constraint - Wish owners are now deleted before the wish itself, respecting the FK constraint
+1 -1
View File
@@ -543,6 +543,6 @@ class IndividualMinifigure(RebrickableMinifigure):
'figure': str(data['set_num']), 'figure': str(data['set_num']),
'number': int(number), 'number': int(number),
'name': str(data['set_name']), 'name': str(data['set_name']),
'image': data.get('set_img_url'), 'image': str(data['set_img_url']) if data.get('set_img_url') else None,
'number_of_parts': int(data.get('num_parts', 0)), 'number_of_parts': int(data.get('num_parts', 0)),
} }
+4 -4
View File
@@ -55,7 +55,7 @@ class RebrickableImage(object):
# Get the URL (this handles nil images via url() method) # Get the URL (this handles nil images via url() method)
url = self.url() url = self.url()
if url is None: if not url:
return return
# Grab the image # Grab the image
@@ -88,7 +88,7 @@ class RebrickableImage(object):
return self.part.fields.image_id return self.part.fields.image_id
if self.minifigure is not None: if self.minifigure is not None:
if self.minifigure.fields.image is None: if not self.minifigure.fields.image:
return RebrickableImage.nil_minifigure_name() return RebrickableImage.nil_minifigure_name()
else: else:
return self.minifigure.fields.figure return self.minifigure.fields.figure
@@ -113,13 +113,13 @@ class RebrickableImage(object):
# Return the url depending on the objects provided # Return the url depending on the objects provided
def url(self, /) -> str: def url(self, /) -> str:
if self.part is not None: if self.part is not None:
if self.part.fields.image is None: if not self.part.fields.image:
return current_app.config['REBRICKABLE_IMAGE_NIL'] return current_app.config['REBRICKABLE_IMAGE_NIL']
else: else:
return self.part.fields.image return self.part.fields.image
if self.minifigure is not None: if self.minifigure is not None:
if self.minifigure.fields.image is None: if not self.minifigure.fields.image:
return current_app.config['REBRICKABLE_IMAGE_NIL_MINIFIGURE'] return current_app.config['REBRICKABLE_IMAGE_NIL_MINIFIGURE']
else: else:
return self.minifigure.fields.image return self.minifigure.fields.image
+1 -1
View File
@@ -110,5 +110,5 @@ class RebrickableMinifigure(BrickRecord):
'number': int(number), 'number': int(number),
'name': str(data['set_name']), 'name': str(data['set_name']),
'quantity': int(data['quantity']), 'quantity': int(data['quantity']),
'image': data['set_img_url'], 'image': str(data['set_img_url']) if data['set_img_url'] else None,
} }
+64 -6
View File
@@ -1,6 +1,6 @@
// Add page - handles both sets and individual minifigures // Add page - handles both sets and individual minifigures
// Server auto-routes fig- numbers to IndividualMinifigure via LOAD_SET / IMPORT_SET
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
// Initialize date pickers
document.querySelectorAll('[data-add-date="true"]').forEach(el => { document.querySelectorAll('[data-add-date="true"]').forEach(el => {
new Datepicker(el, { new Datepicker(el, {
buttonClass: 'btn', buttonClass: 'btn',
@@ -8,23 +8,81 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
}); });
// Get template data from data attributes
const addContainer = document.getElementById('add-set'); const addContainer = document.getElementById('add-set');
if (!addContainer) return; if (!addContainer) return;
new BrickSetSocket( // Read data from data attributes
'add', const templateData = {
addContainer.dataset.path, path: addContainer.dataset.path,
addContainer.dataset.namespace, namespace: addContainer.dataset.namespace,
{ messages: {
COMPLETE: addContainer.dataset.msgComplete, COMPLETE: addContainer.dataset.msgComplete,
FAIL: addContainer.dataset.msgFail, FAIL: addContainer.dataset.msgFail,
IMPORT_SET: addContainer.dataset.msgImportSet, IMPORT_SET: addContainer.dataset.msgImportSet,
LOAD_SET: addContainer.dataset.msgLoadSet, LOAD_SET: addContainer.dataset.msgLoadSet,
PROGRESS: addContainer.dataset.msgProgress, PROGRESS: addContainer.dataset.msgProgress,
SET_LOADED: addContainer.dataset.msgSetLoaded, SET_LOADED: addContainer.dataset.msgSetLoaded,
IMPORT_MINIFIGURE: addContainer.dataset.msgImportMinifigure,
LOAD_MINIFIGURE: addContainer.dataset.msgLoadMinifigure,
MINIFIGURE_LOADED: addContainer.dataset.msgMinifigureLoaded, MINIFIGURE_LOADED: addContainer.dataset.msgMinifigureLoaded,
}
};
// Default: create set socket
const setSocket = new BrickSetSocket(
'add',
templateData.path,
templateData.namespace,
{
COMPLETE: templateData.messages.COMPLETE,
FAIL: templateData.messages.FAIL,
IMPORT_SET: templateData.messages.IMPORT_SET,
LOAD_SET: templateData.messages.LOAD_SET,
PROGRESS: templateData.messages.PROGRESS,
SET_LOADED: templateData.messages.SET_LOADED,
}, },
false, false,
false false
); );
// Override the execute method to check for minifigures
const originalExecute = setSocket.execute.bind(setSocket);
let minifigSocket = null;
setSocket.execute = function() {
const inputValue = document.getElementById('add-set').value.trim();
if (inputValue.startsWith('fig-') || inputValue.match(/^fig\d/i)) {
// It's a minifigure - create minifig socket if needed and execute when ready
if (!minifigSocket) {
minifigSocket = new BrickMinifigureSocket(
'add',
templateData.path,
templateData.namespace,
{
COMPLETE: templateData.messages.COMPLETE,
FAIL: templateData.messages.FAIL,
IMPORT_MINIFIGURE: templateData.messages.IMPORT_MINIFIGURE,
LOAD_MINIFIGURE: templateData.messages.LOAD_MINIFIGURE,
MINIFIGURE_LOADED: templateData.messages.MINIFIGURE_LOADED,
PROGRESS: templateData.messages.PROGRESS,
}
);
// Wait for socket to connect before executing
const checkConnection = setInterval(() => {
if (minifigSocket.socket && minifigSocket.socket.connected) {
clearInterval(checkConnection);
minifigSocket.execute();
}
}, 100);
} else {
minifigSocket.execute();
}
} else {
// It's a set - use original execute
originalExecute();
}
};
}); });