diff --git a/src/app/WardrobePage/Item.js b/src/app/WardrobePage/Item.js
index ad235ac..08b579e 100644
--- a/src/app/WardrobePage/Item.js
+++ b/src/app/WardrobePage/Item.js
@@ -25,6 +25,8 @@ import {
ItemBadgeTooltip,
NcBadge,
NpBadge,
+ YouOwnThisBadge,
+ YouWantThisBadge,
} from "../components/ItemCard";
import SupportOnly from "./support/SupportOnly";
@@ -222,6 +224,8 @@ function ItemBadges({ item }) {
// than try to line things up like a table.
)}
+ {item.currentUserOwnsThis && }
+ {item.currentUserWantsThis && }
{occupiedZoneLabels.map((zoneLabel) => (
))}
diff --git a/src/app/WardrobePage/SearchPanel.js b/src/app/WardrobePage/SearchPanel.js
index 5df3685..2c357da 100644
--- a/src/app/WardrobePage/SearchPanel.js
+++ b/src/app/WardrobePage/SearchPanel.js
@@ -274,6 +274,8 @@ function useSearchResults(query, outfitState) {
name
thumbnailUrl
isNc
+ currentUserOwnsThis
+ currentUserWantsThis
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
# This enables us to quickly show the item when the user clicks it!
diff --git a/src/app/WardrobePage/useOutfitState.js b/src/app/WardrobePage/useOutfitState.js
index 1882202..ab56616 100644
--- a/src/app/WardrobePage/useOutfitState.js
+++ b/src/app/WardrobePage/useOutfitState.js
@@ -38,6 +38,8 @@ function useOutfitState() {
name
thumbnailUrl
isNc
+ currentUserOwnsThis
+ currentUserWantsThis
appearanceOn(speciesId: $speciesId, colorId: $colorId) {
# This enables us to quickly show the item when the user clicks it!
diff --git a/src/app/components/ItemCard.js b/src/app/components/ItemCard.js
index b2e156a..4846115 100644
--- a/src/app/components/ItemCard.js
+++ b/src/app/components/ItemCard.js
@@ -9,7 +9,7 @@ import {
useColorModeValue,
useTheme,
} from "@chakra-ui/core";
-import { StarIcon } from "@chakra-ui/icons";
+import { CheckIcon, StarIcon } from "@chakra-ui/icons";
import { Link } from "react-router-dom";
import { safeImageUrl } from "../util";
@@ -238,20 +238,22 @@ export function NpBadge() {
);
}
-export function YouOwnThisBadge() {
+export function YouOwnThisBadge({ variant = "long" }) {
return (
-
-
- You own this!
+
+
+ {variant === "long" && <>You own this!>}
+ {variant === "short" && <>Own>}
);
}
-export function YouWantThisBadge() {
+export function YouWantThisBadge({ variant = "long" }) {
return (
- You want this!
+ {variant === "long" && <>You want this!>}
+ {variant === "short" && <>Want>}
);
}
diff --git a/src/server/query-tests/Item.test.js b/src/server/query-tests/Item.test.js
index e06f888..e730989 100644
--- a/src/server/query-tests/Item.test.js
+++ b/src/server/query-tests/Item.test.js
@@ -1,5 +1,5 @@
const gql = require("graphql-tag");
-const { query, getDbCalls } = require("./setup.js");
+const { query, getDbCalls, logInAsTestUser } = require("./setup.js");
describe("Item", () => {
it("loads metadata", async () => {
@@ -288,6 +288,98 @@ describe("Item", () => {
`);
});
+ it("loads whether we own/want items", async () => {
+ await logInAsTestUser();
+
+ const res = await query({
+ query: gql`
+ query {
+ items(ids: ["38913", "39945", "39948"]) {
+ id
+ currentUserOwnsThis
+ currentUserWantsThis
+ }
+ }
+ `,
+ });
+
+ expect(res).toHaveNoErrors();
+ expect(res.data).toMatchInlineSnapshot(`
+ Object {
+ "items": Array [
+ Object {
+ "currentUserOwnsThis": false,
+ "currentUserWantsThis": false,
+ "id": "38913",
+ },
+ Object {
+ "currentUserOwnsThis": false,
+ "currentUserWantsThis": true,
+ "id": "39945",
+ },
+ Object {
+ "currentUserOwnsThis": true,
+ "currentUserWantsThis": false,
+ "id": "39948",
+ },
+ ],
+ }
+ `);
+ expect(getDbCalls()).toMatchInlineSnapshot(`
+ Array [
+ Array [
+ "SELECT closet_hangers.*, item_translations.name as item_name FROM closet_hangers
+ INNER JOIN items ON items.id = closet_hangers.item_id
+ INNER JOIN item_translations ON
+ item_translations.item_id = items.id AND locale = \\"en\\"
+ WHERE user_id IN (?)
+ ORDER BY item_name",
+ Array [
+ "44743",
+ ],
+ ],
+ ]
+ `);
+ });
+
+ it("does not own/want items if not logged in", async () => {
+ const res = await query({
+ query: gql`
+ query {
+ items(ids: ["38913", "39945", "39948"]) {
+ id
+ currentUserOwnsThis
+ currentUserWantsThis
+ }
+ }
+ `,
+ });
+
+ expect(res).toHaveNoErrors();
+ expect(res.data).toMatchInlineSnapshot(`
+ Object {
+ "items": Array [
+ Object {
+ "currentUserOwnsThis": false,
+ "currentUserWantsThis": false,
+ "id": "38913",
+ },
+ Object {
+ "currentUserOwnsThis": false,
+ "currentUserWantsThis": false,
+ "id": "39945",
+ },
+ Object {
+ "currentUserOwnsThis": false,
+ "currentUserWantsThis": false,
+ "id": "39948",
+ },
+ ],
+ }
+ `);
+ expect(getDbCalls()).toMatchInlineSnapshot(`Array []`);
+ });
+
it("loads items that need models", async () => {
jest.setTimeout(20000);
diff --git a/src/server/types/Item.js b/src/server/types/Item.js
index ca31e63..4cf1b5e 100644
--- a/src/server/types/Item.js
+++ b/src/server/types/Item.js
@@ -10,6 +10,9 @@ const typeDefs = gql`
rarityIndex: Int!
isNc: Boolean!
+ currentUserOwnsThis: Boolean!
+ currentUserWantsThis: Boolean!
+
# How this item appears on the given species/color combo. If it does not
# fit the pet, we'll return an empty ItemAppearance with no layers.
appearanceOn(speciesId: ID!, colorId: ID!): ItemAppearance!
@@ -91,6 +94,26 @@ const resolvers = {
const item = await itemLoader.load(id);
return item.rarityIndex === 500 || item.rarityIndex === 0;
},
+
+ currentUserOwnsThis: async (
+ { id },
+ _,
+ { currentUserId, userClosetHangersLoader }
+ ) => {
+ if (currentUserId == null) return false;
+ const closetHangers = await userClosetHangersLoader.load(currentUserId);
+ return closetHangers.some((h) => h.itemId === id && h.owned);
+ },
+ currentUserWantsThis: async (
+ { id },
+ _,
+ { currentUserId, userClosetHangersLoader }
+ ) => {
+ if (currentUserId == null) return false;
+ const closetHangers = await userClosetHangersLoader.load(currentUserId);
+ return closetHangers.some((h) => h.itemId === id && !h.owned);
+ },
+
appearanceOn: async (
{ id },
{ speciesId, colorId },