Product
Socket Now Supports uv.lock Files
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
@appmate/wishlist-hydrogen
Advanced tools
Integrate Wishlist King into your Shopify Hydrogen project.
Install package with npm i @appmate/wishlist-hydrogen
.
Setup a Wishlist King client in server.ts
.
// Create a client
const { wishlistKing } = createWishlistClient({
shopDomain: "your-domain.myshopify.com",
// Make sure your session stores the customer id.
customerId: session.get("customerId"),
cache: caches.open("wishlist-king"),
request,
waitUntil,
});
// Add client to request handler
const handleRequest = createRequestHandler({
build: remixBuild,
mode: process.env.NODE_ENV,
getLoadContext: () => ({
// Keep your current context and add wishlistKing
wishlistKing,
}),
});
Create file components/WishlistButton.tsx
and copy the blow sample.
import { useMemo } from "react";
import {
WishlistActionType,
useWishlistContext,
useWishlistProduct,
} from "@appmate/wishlist-hydrogen";
export type WishlistButtonProps = {
productId?: string;
variantId?: string;
};
const WishlistButton = ({ productId = "", variantId }: WishlistButtonProps) => {
const wishlist = useWishlistContext();
const product = useWishlistProduct({
wishlist,
productId,
variantId,
});
const inWishlist = useMemo(() => {
if (
wishlist.pendingAction?.type === WishlistActionType.ADD_TO_WISHLIST &&
product.equals(wishlist.pendingAction.payload)
) {
return true;
}
if (
wishlist.pendingAction?.type ===
WishlistActionType.REMOVE_FROM_WISHLIST &&
product.equals(wishlist.pendingAction.payload)
) {
return false;
}
return product.inWishlist;
}, [wishlist, product]);
return (
<button onClick={product.toggleProduct} disabled={!wishlist.idle}>
{inWishlist ? "Remove from Wishlist" : "Add to Wishlist"}
</button>
);
};
export default WishlistButton;
Create file components/WishlistGrid.tsx
and copy the blow sample.
import WishlistButton from "./WishlistButton";
export type WishlistGridProps = {
products: { id: string; title: string }[];
onLoadMore: () => void;
loading: boolean;
hasNextPage: boolean;
};
const WishlistGrid = ({
products,
onLoadMore,
loading: loadingMore,
hasNextPage: hasMore,
}: WishlistGridProps) => {
return (
<div>
<div>
<h1>Wishlist</h1>
{!products.length ? (
<p>You wishllist is empty!</p>
) : (
<div>
{/* TODO: Render your product cards here */}
{products.map((product) => (
<div key={product.id}>
<p>{product.title}</p>
<WishlistButton productId={product.id} />
</div>
))}
</div>
)}
</div>
{hasMore && (
<button onClick={onLoadMore} disabled={loadingMore}>
Load more
</button>
)}
</div>
);
};
export default WishlistGrid;
Create file components/WishlistLink.tsx
and copy the blow sample.
import { useMemo } from "react";
import { Link } from "@remix-run/react";
import type { LinkProps } from "@remix-run/react";
import {
WishlistActionType,
useWishlistContext,
} from "@appmate/wishlist-hydrogen";
export type WishlistLinkProps = Partial<LinkProps>;
const WishlistLink = ({ ...props }: WishlistLinkProps) => {
const wishlist = useWishlistContext();
const numItems = useMemo(() => {
if (wishlist.pendingAction?.type === WishlistActionType.ADD_TO_WISHLIST) {
return wishlist.data.numItems + 1;
}
if (
wishlist.pendingAction?.type === WishlistActionType.REMOVE_FROM_WISHLIST
) {
return wishlist.data.numItems - 1;
}
return wishlist.data.numItems;
}, [wishlist]);
return (
<Link to="/wishlists/mine" {...props}>
Wishlist ({numItems})
</Link>
);
};
export default WishlistLink;
Render your main layout inside the WishlistProvider
.
<WishlistProvider>{/* Add your layout here */}</WishlistProvider>
Create file routes/($locale).wishlists.$wishlistHandle.tsx
and copy the blow sample.
import { useLoaderData } from "@remix-run/react";
import { ActionArgs, LoaderArgs, json } from "@shopify/remix-oxygen";
import { useMemo } from "react";
import {
useWishlistContext,
useLoadMore,
getProductState,
} from "@appmate/wishlist-hydrogen";
import WishlistGrid from "../components/WishlistGrid";
const PRODUCTS_PER_PAGE = 24;
// TODO: Add your product query required for produt cards.
const PRODUCT_CARDS_QUERY = `
query ProductCardQuery (
$productIds:[ID!]!
$country: CountryCode
$language: LanguageCode
) @inContext(country: $country, language: $language) {
nodes(ids:$productIds){
id
... on Product {
title
}
}
}
`;
export interface ProductCardNodes {
nodes: {
id: string;
title: string;
}[];
}
export async function loader({
params,
request,
context: { storefront, wishlistKing },
}: LoaderArgs) {
const { wishlistHandle = "mine" } = params;
const searchParams = new URL(request.url).searchParams;
const skipProducts = searchParams.get("skip-products");
const { wishlist } = await wishlistKing.loadWishlist({
wishlistId: wishlistHandle,
});
if (skipProducts) {
return json({
wishlist,
products: [],
analytics: null,
pageInfo: {
hasNextPage: !!wishlist.items.length,
startCursor: wishlist.items[0]?.id ?? null,
endCursor: wishlist.items[0]?.id ?? null,
},
});
}
// Paginate
const first = parseInt(
searchParams.get("first") ?? PRODUCTS_PER_PAGE.toString(),
);
const after = searchParams.get("after");
const startIndex = wishlist.items.findIndex((item) => item.id === after) + 1;
const itemsOnPage = wishlist.items.slice(startIndex, startIndex + first);
if (!itemsOnPage.length) {
return json({
wishlist,
products: [],
analytics: null,
pageInfo: {
hasNextPage: false,
startCursor: null,
endCursor: null,
},
});
}
const productIds = [
...new Set(
itemsOnPage.map((item) => `gid://shopify/Product/${item.productId}`),
),
];
const products = await storefront
.query<ProductCardNodes>(PRODUCT_CARDS_QUERY, {
variables: {
productIds,
country: storefront.i18n.country,
language: storefront.i18n.language,
},
storefrontApiVersion: "2023-01",
})
.then((result) =>
result.nodes
.filter((product) => !!product)
.map((product) => ({
...product,
wishlistState: getProductState(wishlist, {
productId: product.id,
}),
})),
);
const firstItemOnPage = itemsOnPage[0];
const lastItemOnPage = itemsOnPage[itemsOnPage.length - 1];
return json({
wishlist,
products,
pageInfo: {
hasNextPage:
wishlist.items[wishlist.items.length - 1].id !== lastItemOnPage.id,
startCursor: firstItemOnPage.id,
endCursor: lastItemOnPage.id,
},
});
}
export async function action(args: ActionArgs) {
const {
request,
context: { wishlistKing },
} = args;
switch (request.method) {
case "POST":
return await wishlistKing.actions.addWishlistItem(args);
case "PUT":
return await wishlistKing.actions.updateWishlistItem(args);
case "DELETE":
return await wishlistKing.actions.removeWishlistItem(args);
}
throw json(
{ message: "Wishlist action method does not exist" },
{ status: 405, statusText: "Method Not Allowed" },
);
}
const WishlistPage = () => {
const loaderData = useLoaderData<typeof loader>();
const wishlist = useWishlistContext();
const {
products: moreProducts,
pageInfo,
loadMore,
loading,
} = useLoadMore<(typeof loaderData)["products"][0]>({
productsPerPage: PRODUCTS_PER_PAGE,
initPage: loaderData.pageInfo,
wishlist,
});
const products = useMemo(
() =>
[...loaderData.products, ...moreProducts.slice(PRODUCTS_PER_PAGE)]
.filter((product) => !!product)
.filter(
(product) =>
product.wishlistState.inWishlist &&
!wishlist.deletedItems.includes(
product.wishlistState.wishlistItemId,
),
),
[loaderData, moreProducts, wishlist],
);
return (
<WishlistGrid
products={products}
hasNextPage={pageInfo.hasNextPage}
onLoadMore={loadMore}
loading={loading}
/>
);
};
export default WishlistPage;
Render WishlistLink
in header.
<WishlistLink />
Render WishlistButton
on product page.
<WishlistButton productId={product.id} variantId={selectedVariant?.id} />
FAQs
Wishlist King SDK for Shopify Hydrogen
We found that @appmate/wishlist-hydrogen demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.