Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: marketplace links in NFT details page #111

Merged
merged 34 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
89c22ae
wip
patricio0312rev Sep 19, 2023
2be2c46
feat: Marketplaces URLs
patricio0312rev Sep 20, 2023
bdfe865
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 20, 2023
7e44d39
chore: update lang file
patricio0312rev Sep 20, 2023
2c61764
feat: implement Marketplaces in NFT details page
patricio0312rev Sep 20, 2023
5ecdc58
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 20, 2023
e052493
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 21, 2023
b162cb5
chore: update lang file
patricio0312rev Sep 21, 2023
682d827
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 22, 2023
07cb8f8
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 25, 2023
3444082
fix: restore snap for TokenBalance
patricio0312rev Sep 25, 2023
d86ecee
fix: TokenBalance snap
patricio0312rev Sep 25, 2023
b114605
feat: extra field for opensea_url in collections model
patricio0312rev Sep 25, 2023
2c7b39e
style: resolve style guide violations
patricio0312rev Sep 25, 2023
2f11289
test: CollectionTest
patricio0312rev Sep 25, 2023
071c021
feat: render marketplaces component if opensea slug is in db
patricio0312rev Sep 25, 2023
a9d5074
fix: SampleNftCollection
patricio0312rev Sep 25, 2023
6bfbd7e
fix: SampleCollection
patricio0312rev Sep 25, 2023
d5b5c62
fix: CollectionData
patricio0312rev Sep 25, 2023
cc50945
fix: CollectionTest
patricio0312rev Sep 25, 2023
af174ff
fix: CollectionTest
patricio0312rev Sep 25, 2023
8b52b84
test: coverage for CollectionTest
patricio0312rev Sep 25, 2023
f773969
style: resolve style guide violations
patricio0312rev Sep 25, 2023
8d366a4
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 25, 2023
f4ef8bc
fix: remove opensea_slug_updated_at from extra fields
patricio0312rev Sep 26, 2023
3e3073f
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 26, 2023
818e841
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 28, 2023
fa55df4
chore: update lang file
patricio0312rev Sep 28, 2023
8c502fa
style: resolve style guide violations
patricio0312rev Sep 28, 2023
eadc13a
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 29, 2023
bd391d3
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Sep 29, 2023
66d9a5d
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Oct 2, 2023
f187e48
Merge branch 'develop' into feat/marketplace-links-to-nfts
patricio0312rev Oct 2, 2023
e058849
chore: rebuild assets
patricio0312rev Oct 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/Data/Collections/CollectionData.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function __construct(
#[WithTransformer(IpfsGatewayUrlTransformer::class)]
public ?string $image,
public ?string $banner,
public ?string $openSeaSlug,
public string $website,
public int $nftsCount,
) {
Expand All @@ -56,6 +57,7 @@ public static function fromModel(Collection $collection): self
* floor_price_decimals: int | null,
* image: string | null,
* banner: string | null,
* opensea_slug: string | null,
* website: string,
* nfts_count: int,
* } $collection
Expand All @@ -72,6 +74,7 @@ public static function fromModel(Collection $collection): self
floorPriceDecimals: $collection->floor_price_decimals,
image: $collection->image,
banner: $collection->banner,
openSeaSlug: $collection->opensea_slug,
website: $collection->website,
nftsCount: $collection->nfts_count,
);
Expand Down
2 changes: 2 additions & 0 deletions app/Data/Collections/CollectionDetailData.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public function __construct(
public ?string $image,
public ?string $banner,
public ?string $bannerUpdatedAt,
public ?string $openSeaSlug,
public ?string $website,
public ?string $twitter,
public ?string $discord,
Expand Down Expand Up @@ -65,6 +66,7 @@ public static function fromModel(Collection $collection, CurrencyCode $currencyC
image: $collection->extra_attributes->get('image'),
banner: $collection->extra_attributes->get('banner'),
bannerUpdatedAt: $collection->extra_attributes->get('banner_updated_at'),
openSeaSlug: $collection->extra_attributes->get('opensea_slug'),
website: $collection->website(defaultToExplorer: false),
twitter: $collection->twitter(),
discord: $collection->discord(),
Expand Down
2 changes: 2 additions & 0 deletions app/Data/Gallery/GalleryCollectionData.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function __construct(
public ?string $image,
public ?string $banner,
public ?string $bannerUpdatedAt,
public ?string $openSeaSlug,
public ?string $website,
public ?int $nftsCount,
) {
Expand All @@ -52,6 +53,7 @@ public static function fromModel(Collection $collection, CurrencyCode $currencyC
image: $collection->extra_attributes->get('image'),
banner: $collection->extra_attributes->get('banner'),
bannerUpdatedAt: $collection->extra_attributes->get('banner_updated_at'),
openSeaSlug: $collection->extra_attributes->get('opensea_slug'),
website: $collection->website(),
nftsCount: $collection->nfts_count ?? null,
);
Expand Down
2 changes: 2 additions & 0 deletions app/Data/Nfts/NftCollectionData.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function __construct(
public string $website,
#[WithTransformer(IpfsGatewayUrlTransformer::class)]
public ?string $image,
public ?string $openSeaSlug,
) {
}

Expand All @@ -40,6 +41,7 @@ public static function fromModel(Collection $collection, CurrencyCode $currencyC
floorPrice: $collection->floor_price,
website: $collection->website(),
image: $collection->extra_attributes->get('image'),
openSeaSlug: $collection->extra_attributes->get('opensea_slug'),
);
}
}
1 change: 1 addition & 0 deletions app/Models/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ public function scopeForCollectionData(Builder $query, User $user = null): Build
DB::raw('tokens.decimals as floor_price_decimals'),
DB::raw(sprintf($extraAttributeSelect, 'image', 'image').' as image'),
DB::raw(sprintf($extraAttributeSelect, 'banner', 'banner').' as banner'),
DB::raw(sprintf($extraAttributeSelect, 'opensea_slug', 'opensea_slug').' as opensea_slug'),
// gets the website url with the same logic used on the `website` method
DB::raw(sprintf('COALESCE(%s, CONCAT(networks.explorer_url, \'%s\', collections.address)) as website', sprintf($extraAttributeSelect, 'website', 'website'), '/token/')),
DB::raw('COUNT(collection_nfts.id) as nfts_count'),
Expand Down
1 change: 0 additions & 1 deletion app/Support/Web3NftHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ public function store(Collection $nfts, bool $dispatchJobs = false): void
'banner' => $nftData->collectionBannerImageUrl,
'banner_updated_at' => $nftData->collectionBannerImageUrl ? $now : null,
'opensea_slug' => $nftData->collectionOpenSeaSlug,

]),
$nftData->mintedBlock,
$nftData->mintedAt?->toDateTimeString(),
Expand Down
18 changes: 18 additions & 0 deletions lang/en/urls.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,22 @@
'nft' => 'https://goerli.etherscan.io/nft/:address/:nftId',
],
],
'marketplaces' => [
'opensea' => [
'collection' => 'https://opensea.io/assets/:network/:address',
'nft' => 'https://opensea.io/assets/:network/:address/:nftId',
],
'rarible' => [
'collection' => 'https://rarible.com/collection/:address/items',
'nft' => 'https://rarible.com/token/:address::nftId',
],
'blur' => [
'collection' => 'https://blur.io/collection/:address',
'nft' => 'https://blur.io/asset/:address/:nftId',
],
'looksrare' => [
'collection' => 'https://looksrare.org/collections/:address',
'nft' => 'https://looksrare.org/collections/:address/:nftId',
],
],
];
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,62 @@ describe("NftHeader", () => {

expect(screen.getByTestId("NftHeader__mobile")).toBeInTheDocument();
});

it("should render marketplaces in mobile if openSeaSlug is not null", () => {
const collection = new NFTCollectionFactory().create({
openSeaSlug: "test-slug",
});

const nft = new NftFactory().withWallet().create({
collection,
});

render(<NftHeader nft={nft} />, { breakpoint: Breakpoint.sm });

expect(screen.getByTestId("NftMarketplaces__Opensea")).toBeInTheDocument();
expect(screen.queryByTestId("NftHeader__mobile__marketplaces_point")).toHaveClass("sm:block");
});

it("should not render marketplaces and its point in mobile view if openSeaSlug is null", () => {
const collection = new NFTCollectionFactory().create({
openSeaSlug: null,
});

const nft = new NftFactory().withWallet().create({
collection,
});

render(<NftHeader nft={nft} />, { breakpoint: Breakpoint.sm });

expect(screen.queryByTestId("NftMarketplaces__Opensea")).not.toBeInTheDocument();
expect(screen.queryByTestId("NftHeader__mobile__marketplaces_point")).not.toHaveClass("sm:block");
});

it("should render marketplaces in desktop if openSeaSlug is not null", () => {
const collection = new NFTCollectionFactory().create({
openSeaSlug: "test-slug",
});

const nft = new NftFactory().withWallet().create({
collection,
});

render(<NftHeader nft={nft} />, { breakpoint: Breakpoint.xl });

expect(screen.getByTestId("NftMarketplaces__Opensea")).toBeInTheDocument();
});

it("should not render marketplaces in desktop if openSeaSlug is null", () => {
const collection = new NFTCollectionFactory().create({
openSeaSlug: null,
});

const nft = new NftFactory().withWallet().create({
collection,
});

render(<NftHeader nft={nft} />, { breakpoint: Breakpoint.xl });

expect(screen.queryByTestId("NftMarketplaces__Opensea")).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import cn from "classnames";
import { useTranslation } from "react-i18next";

import { CollectionDescription } from "@/Components/Collections/CollectionDescription";
import { NftActions } from "@/Components/Collections/Nfts/NftHeader/NftActions";
import { NftBasicInfo } from "@/Components/Collections/Nfts/NftHeader/NftBasicInfo";
import { NftOwner } from "@/Components/Collections/Nfts/NftHeader/NftOwner";
import { Marketplaces } from "@/Components/Marketplaces";
import { Point } from "@/Components/Point";
import { useBreakpoint } from "@/Hooks/useBreakpoint";
import { isTruthy } from "@/Utils/is-truthy";

interface Properties {
nft: App.Data.Nfts.NftData;
Expand Down Expand Up @@ -43,6 +47,18 @@ export const NftHeader = ({
description={nft.collection.description}
linkClassName="font-medium text-sm"
/>

{isTruthy(nft.collection.openSeaSlug) && (
<div className="flex flex-row items-center gap-2">
<Point />
<Marketplaces
type="nft"
nftId={nft.tokenNumber}
address={nft.collection.address}
chainId={nft.collection.chainId}
/>
</div>
)}
</div>
</div>
</div>
Expand All @@ -63,18 +79,39 @@ export const NftHeader = ({
return (
<>
<div
className="flex w-full flex-row items-center justify-center gap-2 border-b border-solid border-theme-secondary-300 bg-theme-secondary-50 px-7 pb-4 pt-4 backdrop-blur xs:px-8 sm:border-none sm:pb-4"
className="flex w-full flex-col items-center justify-center gap-2 border-b border-solid border-theme-secondary-300 bg-theme-secondary-50 px-7 pb-4 pt-4 backdrop-blur xs:px-8 sm:flex-row sm:border-none sm:pb-4"
data-testid="NftHeader__mobile"
>
<NftOwner nft={nft} />
<div className="flex flex-row items-center justify-center gap-2">
<NftOwner nft={nft} />

<Point />
<Point />

<CollectionDescription
name={t("pages.nfts.about_nft")}
description={nft.collection.description}
linkClassName="font-medium text-sm"
/>
<CollectionDescription
name={t("pages.nfts.about_nft")}
description={nft.collection.description}
linkClassName="font-medium text-sm"
/>

<div
className={cn("hidden", {
"sm:block": isTruthy(nft.collection.openSeaSlug),
})}
data-testid="NftHeader__mobile__marketplaces_point"
>
<Point />
</div>
</div>
<div>
{isTruthy(nft.collection.openSeaSlug) && (
<Marketplaces
type="nft"
nftId={nft.tokenNumber}
address={nft.collection.address}
chainId={nft.collection.chainId}
/>
)}
</div>
</div>

<div className="mb-6 flex w-full flex-col gap-4 border-b border-solid border-theme-secondary-300 bg-white pb-6 pt-4">
Expand Down
129 changes: 129 additions & 0 deletions resources/js/Components/Marketplaces.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { render, screen } from "@testing-library/react";
import { t } from "i18next";
import { Marketplaces } from "./Marketplaces";
import NFTCollectionFactory from "@/Tests/Factories/Nfts/NFTCollectionFactory";
import NftFactory from "@/Tests/Factories/Nfts/NftFactory";
import { ExplorerChains } from "@/Utils/Explorer";

describe("Marketplaces", () => {
const collection = new NFTCollectionFactory().create({
chainId: ExplorerChains.EthereumMainnet,
});

const polygonCollection = new NFTCollectionFactory().create({
chainId: ExplorerChains.PolygonMainnet,
});

const nft = new NftFactory().create({
collection,
});

const polygonNft = new NftFactory().create({
collection: polygonCollection,
});

it("should render", () => {
render(
<Marketplaces
address={collection.address}
nftId={nft.tokenNumber}
chainId={collection.chainId}
type="nft"
/>,
);

expect(screen.getByTestId("NftMarketplaces")).toBeInTheDocument();
});

it.each([
[
"NftMarketplaces__Opensea",
t("urls.marketplaces.opensea.nft", {
nftId: nft.tokenNumber,
network: "ethereum",
address: collection.address,
}).toLowerCase(),
],
[
"NftMarketplaces__Rarible",
t("urls.marketplaces.rarible.nft", {
nftId: nft.tokenNumber,
network: "ethereum",
address: collection.address,
}).toLowerCase(),
],
[
"NftMarketplaces__Blur",
t("urls.marketplaces.blur.nft", {
nftId: nft.tokenNumber,
network: "ethereum",
address: collection.address,
}).toLowerCase(),
],
[
"NftMarketplaces__LooksRare",
t("urls.marketplaces.looksrare.nft", {
nftId: nft.tokenNumber,
network: "ethereum",
address: collection.address,
}).toLowerCase(),
],
])("should render %s NFT url for ethereum", (testId, url) => {
render(
<Marketplaces
address={collection.address}
nftId={nft.tokenNumber}
chainId={collection.chainId}
type="nft"
/>,
);

expect(screen.getByTestId(testId)).toHaveAttribute("href", url);
});

it.each([
[
"NftMarketplaces__Opensea",
t("urls.marketplaces.opensea.collection", {
nftId: polygonNft.tokenNumber,
network: "matic",
address: polygonCollection.address,
}).toLowerCase(),
],
[
"NftMarketplaces__Rarible",
t("urls.marketplaces.rarible.collection", {
nftId: polygonNft.tokenNumber,
network: "matic",
address: polygonCollection.address,
}).toLowerCase(),
],
[
"NftMarketplaces__Blur",
t("urls.marketplaces.blur.collection", {
nftId: polygonNft.tokenNumber,
network: "matic",
address: polygonCollection.address,
}).toLowerCase(),
],
[
"NftMarketplaces__LooksRare",
t("urls.marketplaces.looksrare.collection", {
nftId: polygonNft.tokenNumber,
network: "matic",
address: polygonCollection.address,
}).toLowerCase(),
],
])("should render %s collection url for polygon", (testId, url) => {
render(
<Marketplaces
address={polygonCollection.address}
nftId={polygonNft.tokenNumber}
chainId={polygonCollection.chainId}
type="collection"
/>,
);

expect(screen.getByTestId(testId)).toHaveAttribute("href", url);
});
});
Loading
Loading