yay, load pets and junk

This commit is contained in:
Emi Matchu 2014-01-17 10:15:56 -06:00
parent 0e9b76b4d4
commit 72b7ce1ac6
2 changed files with 129 additions and 24 deletions

View file

@ -21,21 +21,26 @@
var Neopia = { var Neopia = {
User: { User: {
get: function(id) { get: function(id) {
return Neopia.getJSON("/users/" + id).then(function(response) { return $.getJSON(Neopia.API_URL + "/users/" + id).then(function(response) {
return response.users[0]; return response.users[0];
}); });
} }
}, },
Customization: { Customization: {
get: function(petId) { request: function(petId, type) {
return Neopia.getJSON("/pets/" + petId + "/customization").then(function(response) { return $.ajax({
return response.custom_pet; dataType: "json",
type: type,
url: Neopia.API_URL + "/pets/" + petId + "/customization"
}); });
},
get: function(petId) {
return this.request(petId, "GET");
},
post: function(petId) {
return this.request(petId, "POST");
} }
}, },
getJSON: function(path) {
return $.getJSON(Neopia.API_URL + path);
},
init: function() { init: function() {
var hostEl = $('meta[name=neopia-host]'); var hostEl = $('meta[name=neopia-host]');
if (!hostEl.length) { if (!hostEl.length) {
@ -52,12 +57,34 @@
var Modeling = { var Modeling = {
_customizationsByPetId: {}, _customizationsByPetId: {},
_customizations: [], _customizations: [],
_itemsById: {},
_items: [], _items: [],
_addCustomization: function(customization) { _addCustomization: function(customization) {
this._customizationsByPetId[customization.name] = customization; // Set all equipped, interesting items' statuses as success and cross
// them off the list.
var itemsById = this._itemsById;
var equippedByZone = customization.custom_pet.equipped_by_zone;
var closetItems = customization.closet_items;
Object.keys(equippedByZone).forEach(function(zoneId) {
var equippedClosetId = equippedByZone[zoneId].closet_obj_id;
var equippedObjectId = closetItems[equippedClosetId].obj_info_id;
if (itemsById.hasOwnProperty(equippedObjectId)) {
// TODO: i18n title
customization.statusByItemId[equippedObjectId] = "success";
itemsById[equippedObjectId].el.find("span[data-body-id=" +
customization.custom_pet.body_id + "]").addClass("modeled")
.attr("title", "You just finished modeling this—thanks so much!");
}
});
this._customizationsByPetId[customization.custom_pet.name] = customization;
this._customizations = this._buildCustomizations(); this._customizations = this._buildCustomizations();
this._update(); this._update();
}, },
_addNewCustomization: function(customization) {
customization.loadingForItemId = null;
customization.statusByItemId = {};
this._addCustomization(customization);
},
_buildCustomizations: function() { _buildCustomizations: function() {
var modelCustomizationsByPetId = this._customizationsByPetId; var modelCustomizationsByPetId = this._customizationsByPetId;
return Object.keys(modelCustomizationsByPetId).map(function(petId) { return Object.keys(modelCustomizationsByPetId).map(function(petId) {
@ -65,24 +92,27 @@
}); });
}, },
_createItems: function($) { _createItems: function($) {
var itemsById = this._itemsById;
this._items = $('#newest-unmodeled-items li').map(function() { this._items = $('#newest-unmodeled-items li').map(function() {
var el = $(this); var el = $(this);
var name = el.find('h2').text(); var item = {
return {
component: React.renderComponent(<ModelForItem itemName={name} />,
el.find('.models').get(0)),
el: el, el: el,
id: el.attr('data-item-id'), id: el.attr('data-item-id'),
name: el.find('h2').text(),
missingBodyIdsPresenceMap: el.find('span[data-body-id]').toArray().reduce(function(map, node) { missingBodyIdsPresenceMap: el.find('span[data-body-id]').toArray().reduce(function(map, node) {
map[$(node).attr('data-body-id')] = true; map[$(node).attr('data-body-id')] = true;
return map; return map;
}, {}) }, {})
}; };
item.component = React.renderComponent(<ModelForItem item={item} />,
el.find('.models').get(0));
itemsById[item.id] = item;
return item;
}).toArray(); }).toArray();
}, },
_loadPetCustomization: function(neopiaPetId) { _loadPetCustomization: function(neopiaPetId) {
return Neopia.Customization.get(neopiaPetId) return Neopia.Customization.get(neopiaPetId)
.done(this._addCustomization.bind(this)) .done(this._addNewCustomization.bind(this))
.fail(function() { .fail(function() {
console.error("couldn't load pet %s", neopiaPetId); console.error("couldn't load pet %s", neopiaPetId);
}); });
@ -100,11 +130,23 @@
_loadManyUsersCustomizations: function(neopiaUserIds) { _loadManyUsersCustomizations: function(neopiaUserIds) {
return neopiaUserIds.map(this._loadUserCustomizations.bind(this)); return neopiaUserIds.map(this._loadUserCustomizations.bind(this));
}, },
_startLoading: function(neopiaPetId, itemId) {
var customization = this._customizationsByPetId[neopiaPetId];
customization.loadingForItemId = itemId;
customization.statusByItemId[itemId] = "loading";
this._update();
},
_stopLoading: function(neopiaPetId, itemId, status) {
var customization = this._customizationsByPetId[neopiaPetId];
customization.loadingForItemId = null;
customization.statusByItemId[itemId] = status;
this._update();
},
_update: function() { _update: function() {
var customizations = this._customizations; var customizations = this._customizations;
this._items.forEach(function(item) { this._items.forEach(function(item) {
var filteredCustomizations = customizations.filter(function(c) { var filteredCustomizations = customizations.filter(function(c) {
return item.missingBodyIdsPresenceMap[c.body_id]; return item.missingBodyIdsPresenceMap[c.custom_pet.body_id];
}); });
item.component.setState({customizations: filteredCustomizations}); item.component.setState({customizations: filteredCustomizations});
}); });
@ -116,6 +158,29 @@
var search = document.location.search; var search = document.location.search;
var users = search.indexOf('=') >= 0 ? search.split('=')[1].split(',') : ''; var users = search.indexOf('=') >= 0 ? search.split('=')[1].split(',') : '';
this._loadManyUsersCustomizations(users); this._loadManyUsersCustomizations(users);
},
model: function(neopiaPetId, itemId) {
var oldCustomization = this._customizationsByPetId[neopiaPetId];
var itemsById = this._itemsById;
this._startLoading(neopiaPetId, itemId);
return Neopia.Customization.post(neopiaPetId)
.done(function(newCustomization) {
// Add this field as null for consistency.
newCustomization.loadingForItemId = null;
// Copy previous statuses.
newCustomization.statusByItemId = oldCustomization.statusByItemId;
// Set the attempted item's status as unworn (to possibly be
// overridden by the upcoming loop in _addCustomization).
newCustomization.statusByItemId[itemId] = "unworn";
// Now, finally, let's overwrite the old customization with the new.
Modeling._addCustomization(newCustomization);
})
.fail(function() {
Modeling._stopLoading(neopiaPetId, itemId, "error");
});
} }
}; };
@ -124,15 +189,15 @@
return {customizations: []}; return {customizations: []};
}, },
render: function() { render: function() {
var itemName = this.props.itemName; var item = this.props.item;
function createModelPet(customization) { function createModelPet(customization) {
return <ModelPet customization={customization} return <ModelPet customization={customization}
itemName={itemName} item={item}
key={customization.name} />; key={customization.custom_pet.name} />;
} }
var sortedCustomizations = this.state.customizations.sort(function(a, b) { var sortedCustomizations = this.state.customizations.sort(function(a, b) {
var aName = a.name.toLowerCase(); var aName = a.custom_pet.name.toLowerCase();
var bName = b.name.toLowerCase(); var bName = b.custom_pet.name.toLowerCase();
if (aName < bName) return -1; if (aName < bName) return -1;
if (aName > bName) return 1; if (aName > bName) return 1;
return 0; return 0;
@ -143,16 +208,38 @@
var ModelPet = React.createClass({ var ModelPet = React.createClass({
render: function() { render: function() {
var petName = this.props.customization.name; var petName = this.props.customization.custom_pet.name;
var itemName = this.props.itemName; var status = this.props.customization.statusByItemId[this.props.item.id];
var loadingForItemId = this.props.customization.loadingForItemId;
var disabled = (status === "loading"
|| status === "success");
if (loadingForItemId !== null && loadingForItemId !== this.props.item.id) {
disabled = true;
}
var itemName = this.props.item.name;
var imageSrc = "http://pets.neopets.com/cpn/" + petName + "/1/1.png"; var imageSrc = "http://pets.neopets.com/cpn/" + petName + "/1/1.png";
// TODO: i18n // TODO: i18n
var title = "Submit " + petName + " as a model, especially if they're " + var title = "Submit " + petName + " as a model, especially if they're " +
"wearing the " + itemName + "!"; "wearing the " + itemName + "!";
return <li><button title={title}> if (status === "success") {
var statusMessage = "Thanks! <3";
} else if (status === "unworn") {
var statusMessage = "Not wearing this item.";
} else if (status === "error") {
var statusMessage = "Couldn't load. Try again?";
} else {
var statusMessage ="";
}
return <li data-status={status}><button onClick={this.handleClick} title={title} disabled={disabled}>
<img src={imageSrc} /> <img src={imageSrc} />
<span>{petName}</span> <div>
<span className="pet-name">{petName}</span>
<span className="message">{statusMessage}</span>
</div>
</button></li>; </button></li>;
},
handleClick: function(e) {
Modeling.model(this.props.customization.custom_pet.name, this.props.item.id);
} }
}); });

View file

@ -220,6 +220,9 @@ body.outfits-new
p p
font-family: $main-font font-family: $main-font
margin-bottom: .5em margin-bottom: .5em
.modeled
color: $soft-text-color
text-decoration: line-through
.models .models
font-size: 85% font-size: 85%
@ -236,17 +239,32 @@ body.outfits-new
background: $module-bg-color background: $module-bg-color
border: 1px solid $module-border-color border: 1px solid $module-border-color
padding-right: 8px padding-right: 8px
text-align: left
&:active &:active
position: relative position: relative
top: 1px top: 1px
img, div
display: inline-block
vertical-align: top
img img
height: 40px height: 40px
margin-right: 8px margin-right: 8px
vertical-align: middle
width: 40px width: 40px
div
line-height: 1.25
padding-top: 10px
.pet-name, .message
display: block
.message
font-style: italic
font-size: 85%
#latest-contribution #latest-contribution
+subtle-banner +subtle-banner